Use of nn.Module
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
- Issue type
- Feature
- Clarity
- Needs clarification
- Activity status
- Stale
- Domain
- machine-learning
Research direction
Start by locating the diffwofost submodules and the main DiffWofost model entry point, then compare their current structure with the nn.Module composition shown in this issue. The issue does not name repository files or tests, and completion would require deciding how contextual parameter values should preserve gradient flow.
Written by the indexing model from the issue text.
Description
Just wanted to post this to clarify my comment about nn.Module this morning and why I think it could be very useful to integrate in the diffwofost submodules as well.
Here is some code that reflects how I currently envisioned the implementation structure. the code is self contained and should be runnable if copied in a colab notebook for example
I'm sure many if not all of these things are known to you but I figured it might help structure the discussion or clarify if I'm missing something.
"""
Toy example of structuring diffwofost
"""
import torch
import torch.nn as nn
# Wofost simulation object
class SimulationObject:
pass
# Some differentiable phenology module
class DiffPhenology(SimulationObject, nn.Module):
def __init__(self, dtype=None, device=None) -> None:
super().__init__()
self._p = nn.Parameter(torch.tensor(1.0, dtype=dtype, device=device,))
def forward(self, x):
return self._p * x
# Some differentiable assimilation module
class DiffPartitioning(SimulationObject, nn.Module):
def __init__(self, dtype=None, device=None) -> None:
super().__init__()
self._p = nn.Parameter(torch.tensor(1.0, dtype=dtype, device=device,))
def forward(self, x):
return self._p * x + torch.ones_like(x, device=x.device, dtype=x.dtype)
# Differentiable wofost that is composed of differentiable submodules and determines how they are related
class DiffWofost(nn.Module):
def __init__(self, dtype=None, device=None) -> None:
super().__init__()
self._module_phenology = DiffPhenology(dtype=dtype, device=device)
self._module_partitioning = DiffPartitioning(dtype=dtype, device=device)
def forward(self, x):
x = self._module_phenology(x)
x = self._module_partitioning(x)
return x
# some function to visualize parameter states
def param_summary(p: torch.Tensor) -> str:
info = [
f'value: {p.item() if sum(p.shape) <= 1 else "-"}',
f'dtype: {p.dtype}',
f'device: {p.device}',
f'requires_grad: {p.requires_grad}',
f'grad: {p.grad if sum(p.shape) <= 1 else "-"}',
f'shape: {tuple(i for i in p.shape)}',
]
return ' | '.join(info)
Using this structure has several benefits that are built-in in pytorch:
- calling
model.toto change tensor dtype or device recursively looks for "submodules" of the model that are alsotorch.nn.Moduleobjects - calling
model.parameters()will list all parameters including those in any submodules
the following code reflects this behavior
model = DiffWofost()
for p in model.parameters():
print(param_summary(p))
if torch.cuda.is_available():
model.to('cuda')
print()
for p in model.parameters():
print(param_summary(p))
model.to(torch.float64)
print()
for p in model.parameters():
print(param_summary(p))
-
Saving model weights become easier since a
model.state_dictcan be obtained that contains all model parameters and can easily be transferred to other sessions/machines -
some commonly used model components (e.g. batchnorm) behave differently during training versus evaluation so pytorch modules have builtin methods
model.trainandmodel.evals.t. model behavior can be set accordingly. callingmodel.trainrecursively calls this to its submodules as well, which could be very useful if some model component is replaced that should take this into account. -
drop in replacement of model components becomes easier if all these things are handled by pytorch, e.g. in the following use-case:
class PhenologyNet(nn.Module):
def __init__(self, dtype=None, device=None) -> None:
super().__init__()
self._p = nn.Parameter(torch.tensor(2.0, dtype=dtype, device=device,))
def forward(self, x):
return self._p * x
model = DiffWofost()
for p in model.parameters():
print(param_summary(p))
del model._module_phenology # Required to de-register old parameters
model._p = PhenologyNet()
print()
for p in model.parameters():
print(param_summary(p))
The model component is updated in-place and model.parameters() adapts accordingly. (dtype and device are still the programmers responsibility)
What could cause problems is changing parameter values "in-place", for example trhough some auxiliary model:
class ParameterModel(nn.Module):
def __init__(self, dtype=None, device=None) -> None:
super().__init__()
self._p = nn.Parameter(torch.tensor(2.0, dtype=dtype, device=device,))
def forward(self, z):
return self._p * z
model = DiffWofost()
x = torch.tensor(2.0)
y_true = torch.tensor(4.0)
loss = abs(model(x) - y_true)
loss.backward()
for p in model.parameters():
print(param_summary(p))
model_p = ParameterModel()
z = torch.tensor(1.0) # Some contextual input to base parameters on
# This raises an error!
model._module_phenology._p.copy_(model_p(z))
print()
for p in model.parameters():
print(param_summary(p))
So here is a model M with parameters P that uses some contextual variables z to "predict" a parameter that should be used by one of the diffwofost components during the forward pass. However, gradients should still flow w.r.t. P. The code above will attempt to do this but will raise an error. I think we can circumvent this but I'm not sure what the best approach is.
Curious to hear your thoughts!
- Dominant language
- Python
- Stars
- 25
- Forks
- 7
- Avg merge
- 14d 3h
- Merged PRs (30d)
- 1
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from WUR-AI/diffWOFOST
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
WUR-AI/diffWOFOST#132 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 42/100
WUR-AI/diffWOFOST#133 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 38/100
WUR-AI/diffWOFOST#129 ·
-
ideas
Difficulty 5/5 Over a week Newbie friendliness 35/100
WUR-AI/diffWOFOST#128 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 58/100
WUR-AI/diffWOFOST#127 ·
All issues in WUR-AI/diffWOFOST
Similar issues
-
bug difficulty:2 good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
MakazhanAlpamys/Soup#1865 ·
Maintainers usually reply within 1 day
-
area: agents area: docs good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
openapi/gknowledge#23 ·
-
useless-suppression reported for deprecated-* messages that should be exemptPossibly taken @adityasinha-ghub claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
pylint-dev/pylint#11656 ·
Maintainers usually reply within 1 day
-
area:provisioning effort:S priority:P2 provider:local-libvirt risk:daytime-only status:in-progress type:bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
randomparity/kdive#3203 · 2 comments ·
Maintainers usually reply within 1 day
-
get() for StrEnumPossibly taken @FeJo5952 claimed this today. Opengood first issue
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
collective/icalendar#1921 · 1 comment ·
Maintainers usually reply within 1 day