libpython-clj icon indicating copy to clipboard operation
libpython-clj copied to clipboard

pytorch lightning Fail

Open niitsuma opened this issue 1 year ago • 3 comments

Following code stop with

AttributeError: 'builtin_function_or_method' object has no attribute 'code'. Did you mean: 'call'?

(ns pytorchlightning.core
  (:gen-class)

  (:require 
   [libpython-clj2.python :as py
    :refer
    [ py. py.- 
     as-jvm
     set-attr!
     get-item
     ->py-tuple
     ->py-list
     ]]
   
   [libpython-clj2.require :refer [require-python]]
   ))

;;(py/initialize!)

(require-python
 '[builtins :as python]
 '[torch]
 '[torch.nn :as nn :refer [Linear]]
 '[torch.nn.functional :refer [mse_loss]]
 '[torch.utils.data :refer [DataLoader Dataset]]
 '[torch.optim]
 '[pytorch_lightning]
 )


(defonce model (atom nil))

(def LitModel
  (py/create-class
   "LitModel" [pytorch_lightning/LightningModule]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self]
       (py. pytorch_lightning/LightningModule  __init__ self)
       (py/set-attr!  self "layer" (Linear 1 1))
       nil))
    
    "forward"
    (py/make-tuple-instance-fn
     (fn [self x] (py. self layer x))
     :arg-converter as-jvm
     :method-name "forward"
     )
    
    "training_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (mse_loss (py/get-item batch 1) (py. self forward (py/get-item batch 0)))
       ))
    "validation_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (mse_loss (py/get-item batch 1) (py. self forward (py/get-item batch 0)))
       ))
    "test_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (mse_loss (py/get-item batch 1) (py. self forward (py/get-item batch 0)))
       ))
    
    "configure_optimizers"
    (py/make-tuple-instance-fn
     (fn [self]
       (torch.optim/SGD
        (py. self parameters)
        :lr 0.02)
       ))
    }))

(def SimpleDataset
  (py/create-class
   "SimpleDataset" [Dataset]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self data] (py/set-attr! self "data" data)  nil))
    "__len__"
    (py/make-tuple-instance-fn 
     (fn [self] (python/len (py.- self data))))
    "__getitem__"
    (py/make-tuple-instance-fn
     (fn [self idx]
       (py/->py-tuple
        [
         (torch/tensor [(py/get-item  (py/get-item (py.- self data) idx) 0)] :dtype torch/float32)
         (torch/tensor [(py/get-item  (py/get-item (py.- self data) idx) 1)] :dtype torch/float32)
         ]
        )
       ))
    }))


(def SimpleDataModule
  (py/create-class
   "SimpleDataModule" [pytorch_lightning/LightningDataModule]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self data] (py/set-attr! self "data" data)  nil))
    
    "train_dataloader"
    (py/make-tuple-instance-fn
     (fn [self]
       (DataLoader (SimpleDataset (py.- self data )) :batch_size 2 :shuffle false)))
    "val_dataloader"
    (py/make-tuple-instance-fn
     (fn [self]
       (DataLoader (SimpleDataset (py.- self data )) :batch_size 2 :shuffle false)))
    "test_dataloader"
    (py/make-tuple-instance-fn
     (fn [self]
       (DataLoader (SimpleDataset (py.- self data )) :batch_size 2 :shuffle false)))
    }))




(defn -main [& args]

  (reset! model (LitModel))

  (def data [[1.0 3.0] [2.0 5.0] [3.0 7.0] [4.0 9.0] [5.0 11.0]] )
  (def data_pylist (py/->py-list data))

  ;; (println  (py/get-item  (py/get-item data_pylist 0) 0))

  (def train_dataset  (SimpleDataset data_pylist))
  (def train_loader (DataLoader train_dataset :batch_size 2 :shuffle false))

  ;; ;;for debug
  ;; (def train_pylist (python/list train_loader))
  ;; (def train_pylist_0 (py/get-item train_pylist 0))
  ;; (println (py. @model training_step train_pylist_0 0) );;;OK.  maybe this part work

  
  (def trainer (pytorch_lightning/Trainer :max_epochs 10 ))

  (def datamodu (SimpleDataModule data_pylist))
  
  (py. trainer fit @model train_loader)
  ;;(py. trainer fit @model datamodu)  ;;also fail

  )


niitsuma avatar Jun 18 '24 12:06 niitsuma

It appears to me that this happens when you pass in a function that doesn't have its source code thus the autodiff system can't autodiff. Keep in mind that functions defined via clojure are translated to python as opaque C function pointers so they won't be autodifferentiable.

cnuernber avatar Sep 14 '24 14:09 cnuernber

@cnuernber Thank you for the awesome project! __code__ is used in the following parts, and it is likely only to check whether instance_method has been overridden as intended. Now, I would be happy if there were a way to give a fake __code__ value to a function represented by a C pointer—something like (set-attr! func "__code__" ""). Do you have any good ideas?

https://github.com/Lightning-AI/utilities/blob/b2359648d26af67d5f97fd8e00c47103c976a485/src/lightning_utilities/core/overrides.py#L9-L34

https://github.com/Lightning-AI/torchmetrics/blob/c57197e7e68e226aefb28f4496a751555074c352/src/torchmetrics/utilities/checks.py#L740-L762

Execution error at libpython-clj2.python.ffi/check-error-throw (ffi.clj:717).
Traceback (most recent call last):
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/trainer.py", line 539, in fit
    call._call_and_handle_interrupt(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self, self._fit_impl, model, train_dataloaders, val_dataloaders, datamodule, ckpt_path
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/call.py", line 47, in _call_and_handle_interrupt
    return trainer_fn(*args, **kwargs)
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/trainer.py", line 575, in _fit_impl
    self._run(model, ckpt_path=ckpt_path)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/trainer.py", line 932, in _run
    _verify_loop_configurations(self)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/configuration_validator.py", line 36, in _verify_loop_configurations
    __verify_train_val_loop_configuration(trainer, model)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/trainer/configuration_validator.py", line 51, in __verify_train_val_loop_configuration
    has_training_step = is_overridden("training_step", model)
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning/pytorch/utilities/model_helpers.py", line 46, in is_overridden
    return _is_overridden(method_name, instance, parent)
  File "/Users/tani/Documents/libpython-clj/.venv/lib/python3.13/site-packages/lightning_utilities/core/overrides.py", line 34, in is_overridden
    return instance_attr.__code__ != parent_attr.__code__
           ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'builtin_function_or_method' object has no attribute '__code__'. Did you mean: '__call__'?
(ns sample.core
  (:require
   [libpython-clj2.require :refer [require-python]]
   [libpython-clj2.python :as py :refer [py. py.- py.. py* py**]]
   ))

(py/initialize! :python-executable "/Users/tani/Documents/libpython-clj/.venv/bin/python")

(require-python
 'os
 '[torch.nn :refer [Linear ReLU Sequential]]
 '[torch.nn.functional :refer [mse_loss]]
 '[torch.optim :as o :refer [Adam]]
 '[torch.utils.data :refer [DataLoader]]
 '[lightning :refer [LightningModule Trainer]]
 '[torchvision.datasets :refer [MNIST]]
 '[torchvision.transforms :refer [ToTensor]])

(def encoder (Sequential (Linear (* 28 28) 64) (ReLU) (Linear 64 3)))
(def decoder (Sequential (Linear 64 3) (ReLU)  (Linear (* 28 28) 64)))
(def LitModel
  (py/create-class
   "LitModel" [LightningModule]
   {"__init__"
    (py/make-tuple-instance-fn
     (fn [self encoder decoder]
       (py. LightningModule __init__ self)
       (py/set-attr! self "encoder" encoder)
       (py/set-attr! self "decoder" decoder)
       nil))
    "training_step"
    (py/make-tuple-instance-fn
     (fn [self batch batch_idx]
       (let [head (py/get-item batch 0)
             x (py. head view (py. head size 0) -1)
             z (py. self encoder x)
             xhat (py. self decoder z)
             loss (mse_loss xhat x)]
         loss)))
    "configure_optimizers"
    (py/make-tuple-instance-fn
     (fn [self]
       (py** Adam (py. self parameters) {:lr 0.0001})))}))

(def model (LitModel encoder decoder))
(def dataset (MNIST (os/getcwd) :download true :transform (ToTensor)))
(def train_loader (DataLoader dataset))
(def trainer (Trainer :limit_train_batches 2 :max_epochs 1 :logger []))
(py. trainer fit (py/as-python model) train_loader)

tani avatar Feb 03 '25 09:02 tani

I asked grok chatbot about this. It told me to create a subclass of torch.autograd.Function with libpython-clj as below.

import torch
from torch.autograd import Function
from ctypes import cdll

# Load the C library
lib = cdll.LoadLibrary("./mylib.so")
lib.double_value.argtypes = [ctypes.c_float]
lib.double_value.restype = ctypes.c_float

# Custom autograd function
class DoubleFunction(Function):
    @staticmethod
    def forward(ctx, input):
        # Extract scalar value from tensor, call C function
        scalar_input = input.item()
        result = lib.double_value(scalar_input)
        # Return as a tensor
        output = torch.tensor([result], dtype=input.dtype, device=input.device)
        # Save input for backward pass if needed
        ctx.save_for_backward(input)
        return output

    @staticmethod
    def backward(ctx, grad_output):
        # Retrieve saved input if needed (here, not strictly necessary)
        input, = ctx.saved_tensors
        # Gradient of doubling is 2, multiplied by incoming gradient
        grad_input = grad_output * 2
        return grad_input

# Python wrapper for convenience
def double_tensor(x):
    return DoubleFunction.apply(x)

Another way is to replace pytorch lightning with pytorch.

amano-kenji avatar Mar 28 '25 11:03 amano-kenji