pytorch lightning Fail
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
)
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 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)
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.