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)# 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
| import torch | |
| from torch import nn | |
| from transformers import PreTrainedModel | |
| from transformers.modeling_outputs import SequenceClassifierOutput | |
| from .configuration_yield import YieldConfig | |
| from .yield_transformer import YieldTransformer | |
| class YieldForSequenceClassification(PreTrainedModel): | |
| config_class = YieldConfig | |
| base_model_prefix = "yield_model" | |
| def __init__(self, config: YieldConfig): | |
| super().__init__(config) | |
| self.yield_model = YieldTransformer( | |
| w_dim=config.W, | |
| soil_dim=config.S, | |
| d_model=config.d_model, | |
| nhead=config.nhead, | |
| num_layers=config.num_layers, | |
| dim_ff=config.dim_ff, | |
| dropout=config.dropout, | |
| use_crop=config.use_crop, | |
| crop_emb_dim=config.crop_emb_dim, | |
| max_weeks=max(52, config.K), | |
| pool=config.pool, | |
| ) | |
| self.post_init() | |
| def forward( | |
| self, | |
| weather, | |
| soil, | |
| crop_id, | |
| horizon_idx=None, | |
| labels=None, | |
| **kwargs, | |
| ): | |
| # ================================================== | |
| # Shape checks | |
| # ================================================== | |
| if weather.ndim != 3: | |
| raise ValueError( | |
| f"weather must have shape [B,K,W], " | |
| f"received {tuple(weather.shape)}" | |
| ) | |
| if soil.ndim != 2: | |
| raise ValueError( | |
| f"soil must have shape [B,S], " | |
| f"received {tuple(soil.shape)}" | |
| ) | |
| # ================================================== | |
| # Training normalization statistics | |
| # ================================================== | |
| w_mean = torch.tensor( | |
| self.config.w_mean, | |
| device=weather.device, | |
| dtype=weather.dtype, | |
| ) | |
| w_std = torch.tensor( | |
| self.config.w_std, | |
| device=weather.device, | |
| dtype=weather.dtype, | |
| ) | |
| s_mean = torch.tensor( | |
| self.config.s_mean, | |
| device=soil.device, | |
| dtype=soil.dtype, | |
| ) | |
| s_std = torch.tensor( | |
| self.config.s_std, | |
| device=soil.device, | |
| dtype=soil.dtype, | |
| ) | |
| # NaN -> training mean | |
| weather = torch.where( | |
| torch.isnan(weather), | |
| w_mean.view(1, 1, -1), | |
| weather, | |
| ) | |
| soil = torch.where( | |
| torch.isnan(soil), | |
| s_mean.view(1, -1), | |
| soil, | |
| ) | |
| # normalize | |
| weather = ( | |
| weather - w_mean.view(1, 1, -1) | |
| ) / w_std.view(1, 1, -1) | |
| soil = ( | |
| soil - s_mean.view(1, -1) | |
| ) / s_std.view(1, -1) | |
| # ================================================== | |
| # Cutoff | |
| # ================================================== | |
| if horizon_idx is None: | |
| horizon_idx = torch.full( | |
| (weather.shape[0],), | |
| weather.shape[1], | |
| dtype=torch.long, | |
| device=weather.device, | |
| ) | |
| if not torch.is_tensor(horizon_idx): | |
| horizon_idx = torch.tensor( | |
| horizon_idx, | |
| dtype=torch.long, | |
| device=weather.device, | |
| ) | |
| horizon_idx = horizon_idx.to( | |
| weather.device | |
| ).long() | |
| if horizon_idx.ndim == 0: | |
| horizon_idx = horizon_idx.unsqueeze(0) | |
| # FlexServe requests are normally one sample. | |
| # Ensure one common temporal length for a batch. | |
| unique_cutoffs = torch.unique( | |
| horizon_idx | |
| ) | |
| if len(unique_cutoffs) != 1: | |
| raise ValueError( | |
| "All samples in one batch must use the same cutoff." | |
| ) | |
| t_eff = int( | |
| unique_cutoffs[0].item() | |
| ) | |
| weather = weather[ | |
| :, | |
| :t_eff, | |
| : | |
| ] | |
| # ================================================== | |
| # Existing trained model | |
| # ================================================== | |
| logits_norm = self.yield_model( | |
| weather, | |
| soil, | |
| crop_id, | |
| horizon_idx=horizon_idx, | |
| causal=True, | |
| return_sequence=False, | |
| ) | |
| # Restore yield to bu/acre. | |
| y_mean = torch.tensor( | |
| self.config.y_mean, | |
| device=logits_norm.device, | |
| dtype=logits_norm.dtype, | |
| ) | |
| y_std = torch.tensor( | |
| self.config.y_std, | |
| device=logits_norm.device, | |
| dtype=logits_norm.dtype, | |
| ) | |
| predicted_yield = ( | |
| logits_norm * y_std | |
| + y_mean | |
| ) | |
| # TextClassificationPipeline expects [B, num_labels]. | |
| logits = predicted_yield.unsqueeze(-1) | |
| loss = None | |
| if labels is not None: | |
| labels = labels.to( | |
| logits.dtype | |
| ).view(-1) | |
| loss = nn.functional.mse_loss( | |
| predicted_yield, | |
| labels, | |
| ) | |
| return SequenceClassifierOutput( | |
| loss=loss, | |
| logits=logits, | |
| ) |