Text Classification
Transformers
Safetensors
yield-weather-soil
crop-yield
multi-temporal
regression
yield-estimation
custom_code
Instructions to use ICICLE-AI/yield-estimation with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use ICICLE-AI/yield-estimation with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="ICICLE-AI/yield-estimation", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForSequenceClassification model = AutoModelForSequenceClassification.from_pretrained("ICICLE-AI/yield-estimation", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 5,128 Bytes
fb4abf6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | import torch
import torch.nn as nn
class MLP(nn.Module):
def __init__(self, in_dim: int, out_dim: int, hidden: int = 128, dropout: float = 0.05):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, hidden//2),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(hidden//2, hidden),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(hidden, out_dim)
)
def forward(self, x):
return self.net(x)
class YieldTransformer(nn.Module):
def __init__(
self,
w_dim: int,
soil_dim: int,
d_model: int = 128,
nhead: int = 4,
num_layers: int = 2,
dim_ff: int = 256,
dropout: float = 0.05,
use_crop: bool = True,
crop_emb_dim: int = 8,
max_weeks: int = 32,
pool: str = "mean", # Changed default to mean
farm_emb_dim: int = 8,
horizon_emb_dim: int = 16,
):
super().__init__()
assert pool in ["last", "cls", "mean"]
self.pool = pool
self.use_crop = use_crop
self.max_weeks = max_weeks
crop_in = crop_emb_dim if use_crop else 0
if use_crop:
self.crop_emb = nn.Embedding(2, crop_emb_dim)
self.horizon_emb = nn.Embedding(max_weeks + 1, horizon_emb_dim)
self.horizon_proj = nn.Linear(horizon_emb_dim, d_model)
self.week_proj = nn.Linear(1, d_model)
self.weather_enc = MLP(w_dim + crop_in, d_model, hidden=d_model, dropout=dropout)
self.soil_enc = MLP(soil_dim + crop_in, d_model, hidden=d_model, dropout=dropout)
self.ws_cross_attn = nn.MultiheadAttention(
embed_dim=d_model,
num_heads=nhead,
dropout=dropout,
batch_first=True,
)
self.ws_ln = nn.LayerNorm(d_model)
self.pos_emb = nn.Parameter(torch.zeros(1, max_weeks + 1, d_model))
nn.init.trunc_normal_(self.pos_emb, std=0.02)
if pool == "cls":
self.cls_token = nn.Parameter(torch.zeros(1, 1, d_model))
nn.init.trunc_normal_(self.cls_token, std=0.02)
enc_layer = nn.TransformerEncoderLayer(
d_model=d_model, nhead=nhead, dim_feedforward=dim_ff,
dropout=dropout, activation="gelu", batch_first=True, norm_first=True,
)
self.temporal_tf = nn.TransformerEncoder(enc_layer, num_layers=num_layers)
self.head = nn.Sequential(
nn.LayerNorm(d_model + horizon_emb_dim + d_model),
nn.Linear(d_model + horizon_emb_dim + d_model, d_model),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(d_model, 1),
)
def forward(
self,
weather,
soil,
crop_id,
farm_field_encoded=None,
week_mask=None,
causal=False,
return_sequence=False,
horizon_idx=None,
):
B, t, W = weather.shape
device = weather.device
if horizon_idx is None: horizon_idx = torch.tensor(t, device=device).expand(B)
if not torch.is_tensor(horizon_idx): horizon_idx = torch.tensor(horizon_idx, device=device).expand(B)
h_idx = horizon_idx.long().clamp(min=1, max=self.max_weeks)
h_emb = self.horizon_emb(h_idx)
h_tok = self.horizon_proj(h_emb)
if self.use_crop:
c = self.crop_emb(crop_id)
weather_in = torch.cat([weather, c[:, None, :].expand(-1, t, -1)], dim=-1)
soil_in = torch.cat([soil, c], dim=-1)
else:
weather_in, soil_in = weather, soil
w_tok = self.weather_enc(weather_in)
s_encoded = self.soil_enc(soil_in)
fused = self.ws_ln(w_tok + s_encoded[:, None, :])
fused = fused + h_tok[:, None, :]
weeks = torch.arange(1, t + 1, device=device).float() / self.max_weeks
week_signal = self.week_proj(weeks[None, :, None].expand(B, -1, -1))
x = fused + week_signal + self.pos_emb[:, :t, :]
if self.pool == "cls":
x = torch.cat([self.cls_token.expand(B, -1, -1), x], dim=1)
t_idx = t + 1
else: t_idx = t
src_key_padding_mask = ~week_mask.bool() if week_mask is not None else None
attn_mask = None
if causal:
L = x.size(1)
attn_mask = torch.triu(
torch.ones(L, L, device=device, dtype=torch.bool),
diagonal=1,
)
h = self.temporal_tf(
x,
mask=attn_mask,
src_key_padding_mask=src_key_padding_mask,
)
if self.pool == "mean":
if week_mask is not None:
mask = week_mask.unsqueeze(-1).float()
pooled = (h * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1e-9)
else:
pooled = h.mean(dim=1)
elif self.pool == "cls":
pooled = h[:, 0, :]
else:
pooled = h[:, -1, :]
out = torch.cat([pooled, h_emb, s_encoded], dim=-1)
return self.head(out).squeeze(-1)
|