ITPub博客

首页 > 应用开发 > IT综合 > BentoML在农业科技中的应用:智能作物监测与产量预测系统构建

BentoML在农业科技中的应用:智能作物监测与产量预测系统构建

投稿 IT综合 作者:bhluo 时间:2025-11-26 11:29:53 0 删除 编辑

# BentoML在农业科技中的应用:智能作物监测与产量预测系统构建


在精准农业快速发展的今天,人工智能技术正深刻改变着传统农业生产方式。BentoML作为专业的机器学习模型部署平台,为农业科技应用提供了强大的技术支撑。本文将详细介绍基于BentoML的智能农业监测系统构建方法。


## 系统架构与技术栈


### 整体架构设计


智能作物监测系统采用微服务架构,整合多源农业数据:


```python

from bentoml import BentoService, api, artifacts

from bentoml.adapters import DataframeInput, JsonInput

from bentoml.types import InferenceTask, InferenceError

from bentoml.frameworks.sklearn import SklearnModelArtifact

from bentoml.frameworks.pytorch import PytorchModelArtifact

import pandas as pd

import numpy as np


class AgricultureAISystem(BentoService):

    """

    农业智能监测系统核心服务

    """

    

    def __init__(self):

        super().__init__()

        self.weather_model = None

        self.yield_model = None

        self.disease_model = None

        

    @api(input=DataframeInput(), batch=True)

    def predict_yield(self, df: pd.DataFrame):

        """作物产量预测接口"""

        try:

            # 数据预处理

            processed_data = self.preprocess_yield_data(df)

            

            # 特征工程

            features = self.extract_yield_features(processed_data)

            

            # 模型预测

            predictions = self.yield_model.predict(features)

            

            # 后处理

            result = self.postprocess_predictions(predictions, df)

            

            return result

            

        except Exception as e:

            return InferenceError(err_msg=str(e))

```


### 数据采集与处理模块


```python

import asyncio

from datetime import datetime, timedelta

from typing import List, Dict, Optional

import aiohttp


class AgricultureDataCollector:

    """农业数据采集器"""

    

    def __init__(self):

        self.sensor_data_cache = {}

        self.weather_data_cache = {}

        self.satellite_data_cache = {}

        

    async def collect_sensor_data(self, farm_id: str) -> Dict:

        """采集物联网传感器数据"""

        sensor_endpoints = {

            'soil_moisture': f'http://sensors/{farm_id}/moisture',

            'temperature': f'http://sensors/{farm_id}/temperature',

            'humidity': f'http://sensors/{farm_id}/humidity',

            'nutrient_level': f'http://sensors/{farm_id}/nutrients'

        }

        

        async with aiohttp.ClientSession() as session:

            tasks = []

            for sensor_type, endpoint in sensor_endpoints.items():

                task = self.fetch_sensor_data(session, endpoint, sensor_type)

                tasks.append(task)

            

            results = await asyncio.gather(*tasks, return_exceptions=True)

            

            # 整合传感器数据

            sensor_data = {}

            for result in results:

                if isinstance(result, dict):

                    sensor_data.update(result)

            

            return sensor_data

    

    async def fetch_satellite_imagery(self, coordinates: Dict, date_range: tuple) -> List:

        """获取卫星遥感数据"""

        api_url = "https://api.satellite-imagery.com/v1/agriculture"

        params = {<"XIJIA.1853.HK">

            'lat': coordinates['latitude'],

            'lon': coordinates['longitude'],

            'start_date': date_range[0],

            'end_date': date_range[1],

            'data_types': ['ndvi', 'evi', 'lai']  # 植被指数

        }

        

        async with aiohttp.ClientSession() as session:

            async with session.get(api_url, params=params) as response:

                if response.status == 200:

                    data = await response.json()

                    return self.process_satellite_data(data)

                else:

                    raise Exception(f"卫星数据获取失败: {response.status}")

    

    def process_satellite_data(self, raw_data: Dict) -> List:

        """处理卫星遥感数据"""

        processed_data = []

        

        for data_point in raw_data.get('data', []):

            processed_point = {

                'timestamp': data_point['timestamp'],

                'ndvi': data_point.get('ndvi', 0),  # 归一化植被指数

                'evi': data_point.get('evi', 0),    # 增强型植被指数

                'lai': data_point.get('lai', 0),    # 叶面积指数

                'health_score': self.calculate_crop_health(

                    data_point.get('ndvi', 0),

                    data_point.get('evi', 0)

                )

            }

            processed_data.append(processed_point)

        

        return processed_data

```


## 核心模型开发


### 产量预测模型


```python

import torch

import torch.nn as nn

from sklearn.ensemble import RandomForestRegressor

from sklearn.preprocessing import StandardScaler

import joblib


class CropYieldPredictor(nn.Module):

    """作物产量预测神经网络模型"""

    

    def __init__(self, input_size: int, hidden_size: int = 128):

        super().__init__()

        self.encoder = nn.Sequential(

            nn.Linear(input_size, hidden_size),

            nn.ReLU(),

            nn.BatchNorm1d(hidden_size),

            nn.Dropout(0.3),

            

            nn.Linear(hidden_size, hidden_size // 2),

            nn.ReLU(),

            nn.BatchNorm1d(hidden_size // 2),

            nn.Dropout(0.2),

            

            nn.Linear(hidden_size // 2, hidden_size // 4),

            nn.ReLU()

        )

        

        self.regressor = nn.Sequential(

            nn.Linear(hidden_size // 4, 64),

            nn.ReLU(),

            nn.Linear(64, 32),

            nn.ReLU(),

            nn.Linear(32, 1)  # 预测产量

        )

        

        self.attention = nn.MultiheadAttention(

            embed_dim=hidden_size // 4, 

            num_heads=4

        )

    

    def forward(self, x):

        # 编码输入特征

        encoded = self.encoder(x)

        

        # 注意力机制

        encoded = encoded.unsqueeze(0)  # 添加序列维度

        attended, _ = self.attention(encoded, encoded, encoded)

        attended = attended.squeeze(0)

        

        # 回归预测

        output = self.regressor(attended)

        return output


class YieldPredictionService(BentoService):

    """产量预测BentoML服务"""

    

    def __init__(self):

        super().__init__()

        

    @artifacts([

        SklearnModelArtifact('feature_scaler'),

        PytorchModelArtifact('yield_model')

    ])

    @api(input=DataframeInput(), batch=True)

    def predict(self, df: pd.DataFrame):

        """产量预测主接口"""

        try:

            # 特征预处理

            features = self.preprocess_features(df)

            

            # 特征缩放

            scaled_features = self.artifacts.feature_scaler.transform(features)

            

            # 转换为Tensor

            input_tensor = torch.FloatTensor(scaled_features)

            

            # 模型预测

            with torch.no_grad()<"XJ.1853.HK">:

                predictions = self.artifacts.yield_model(input_tensor)

            

            # 转换为numpy并后处理

            yield_predictions = predictions.numpy().flatten()

            result = self.format_predictions(df, yield_predictions)

            

            return result

            

        except Exception as e:

            return {"error": str(e)}

    

    def preprocess_features(self, df: pd.DataFrame) -> np.ndarray:

        """特征预处理"""

        required_features = [

            'soil_moisture', 'temperature', 'humidity', 

            'nutrient_n', 'nutrient_p', 'nutrient_k',

            'ndvi', 'evi', 'lai', 'growth_stage',

            'planting_density', 'irrigation_level'

        ]

        

        # 确保所有必需特征存在

        for feature in required_features:

            if feature not in df.columns:

                df[feature] = 0.0  # 默认值

        

        return df[required_features].values

    

    def format_predictions(self, original_df: pd.DataFrame, predictions: np.ndarray) -> Dict:

        """格式化预测结果"""

        result = {

            'predictions': [],

            'metadata': {

                'model_version': '1.0',

                'timestamp': datetime.now().isoformat(),

                'total_predictions': len(predictions)

            }

        }

        

        for i, (_, row) in enumerate(original_df.iterrows()):

            prediction_data = {

                'field_id': row.get('field_id', f'field_{i}'),

                'crop_type': row.get('crop_type', 'unknown'),

                'predicted_yield': float(predictions[i]),

                'yield_units': 'kg_per_hectare',

                'confidence_interval': self.calculate_confidence(predictions[i]),

                'recommendations': self.generate_recommendations(row, predictions[i])

            }

            result['predictions'].append(prediction_data)

        

        return result

    

    def calculate_confidence(self, prediction: float) -> Dict:

        """计算预测置信区间"""

        # 基于历史误差统计计算置信区间

        std_dev = prediction * 0.15  # 假设15%的标准差

        return {

            'lower_bound': max(0, prediction - 1.96 * std_dev),

            'upper_bound': prediction + 1.96 * std_dev,

            'confidence_level': 0.95

        }

```


### 病虫害检测模型


```python

import cv2

import torchvision.transforms as transforms

from PIL import Image


class CropDiseaseDetector(BentoService):

    """作物病虫害检测服务"""

    

    def __init__(self):

        super().__init__()

        

    @artifacts([PytorchModelArtifact('disease_model')])

    @api(input=JsonInput(), batch=False)

    def detect_disease(self, input_data: Dict):

        """病虫害检测接口"""

        try:

            image_data = input_data.get('image')

            field_id = input_data.get('field_id')

            

            # 解码图像数据

            image = self.decode_image(image_data)

            

            # 图像预处理

            processed_image = self.preprocess_image(image)

            

            # 模型推理

            disease_prediction = self.predict_disease(processed_image)

            

            # 生成诊断报告

            report = self.generate_diagnosis_report(disease_prediction, field_id)

            

            return report

            

        except Exception as e:

            return {"error": f"病虫害检测失败: {str(e)}"}

    

    def decode_image(self, image_data: str) -> Image.Image:

        """解码Base64图像数据"""

        if image_data.startswith('data:image'):

            # 处理data URL格式

            image_data = image_data.split(',')[1]

        

        import base64

        image_bytes = base64.b64decode(image_data)

        image = Image.open(io.BytesIO(image_bytes))

        return image

    

    def preprocess_image(self, image: Image.Image) -> torch.Tensor:

        """图像预处理"""

        transform = transforms.Compose([

            transforms.Resize((224, 224)),

            transforms.ToTensor(),

            transforms.Normalize(

                mean=[0.485, 0.456, 0.406],

                std=[0.229, 0.224, 0.225]<"XJIA.1853.HK">

            )

        ])

        

        return transform(image).unsqueeze(0)  # 添加batch维度

    

    def predict_disease(self, image_tensor: torch.Tensor) -> Dict:

        """执行病虫害预测"""

        with torch.no_grad():

            output = self.artifacts.disease_model(image_tensor)

            probabilities = torch.softmax(output, dim=1)

            

            # 获取预测结果

            predicted_class = torch.argmax(probabilities, dim=1).item()

            confidence = probabilities[0][predicted_class].item()

            

            return {

                'disease_class': predicted_class,

                'disease_name': self.get_disease_name(predicted_class),

                'confidence': confidence,

                'all_probabilities': probabilities.numpy().tolist()

            }

    

    def generate_diagnosis_report(self, prediction: Dict, field_id: str) -> Dict:

        """生成病虫害诊断报告"""

        disease_info = {

            'healthy': {'severity': 'none', 'treatment': '无需处理'},

            'powdery_mildew': {'severity': 'medium', 'treatment': '施用杀菌剂'},

            'leaf_rust': {'severity': 'high', 'treatment': '立即处理'},

            'aphid_infestation': {'severity': 'medium', 'treatment': '生物防治'}

        }

        

        disease_name = prediction['disease_name']

        info = disease_info.get(disease_name, {'severity': 'unknown', 'treatment': '请咨询专家'})

        

        return {

            'field_id': field_id,

            'diagnosis': {

                'disease_detected': disease_name,

                'confidence_score': prediction['confidence'],

                'severity_level': info['severity'],

                'recommended_treatment': info['treatment'],

                'detection_timestamp': datetime.now().isoformat()

            },

            'monitoring_advice': self.get_monitoring_advice(disease_name)

        }

```


## 系统部署与运维


### BentoML服务打包


```python

# build_bento.py

def build_agriculture_services():

    """构建农业AI服务包"""

    

    # 产量预测服务

    yield_service = YieldPredictionService()

    yield_service.pack('feature_scaler', joblib.load('models/scaler.pkl'))

    yield_service.pack('yield_model', torch.load('models/yield_model.pth'))

    

    # 病虫害检测服务

    disease_service = CropDiseaseDetector()

    disease_service.pack('disease_model', torch.load('models/disease_model.pth'))

    

    # 保存Bento

    saved_yield_path = yield_service.save()

    saved_disease_path = disease_service.save()

    

    print(f"产量预测服务保存至: {saved_yield_path}")

    print(f"病虫害检测服务保存至: {saved_disease_path}")

    

    return saved_yield_path, saved_disease_path


def create_docker_compose():

    """生成Docker编排文件"""

    docker_compose = """

version: '3.8'

services:

  yield-predictor:

    image: agriculture-ai/yield-predictor:latest

    ports:

      - "3000:3000"

    environment:

      - BENTOML_CONFIG=/home/bentoml/bentoml_config.yml

    volumes:

      - ./data:/home/bentoml/data

    deploy:

      resources:

        limits:

          memory: 2G

          cpus: '1.0'


  disease-detector:

    image: agriculture-ai/disease-detector:latest

    ports:

      - "3001:3001"

    environment:

      - BENTOML_CONFIG=/home/bentoml/bentoml_config.yml

    volumes:

      - ./image_data:/home/bentoml/images

    deploy:

      resources:

        limits:

          memory: 1G

          cpus: '0.5'


  agriculture-gateway:

    image: nginx:latest

    ports:

      - "80:80"

    volumes:

      - ./nginx.conf:/etc/nginx/nginx.conf

    depends_on:

      - yield-predictor

      - disease-detector

"""

    

    with open('docker-compose.yml', 'w') as f:

        f.write(docker_compose)

```


### 监控与日志系统


```python

import logging

from prometheus_client import Counter, Histogram, generate_latest

import time


class AgricultureMonitor:

    """农业系统监控"""

    

    def __init__(self):

        # 定义监控指标

        self.predict_requests = Counter(

            'yield_predict_requests_total',

            'Total yield prediction requests',

            ['status']<"XIJIAA.1853.HK">

        )

        

        self.prediction_duration = Histogram(

            'yield_prediction_duration_seconds',

            'Yield prediction duration'

        )

        

        self.disease_detections = Counter(

            'disease_detections_total',

            'Total disease detections',

            ['disease_type', 'severity']

        )

    

    def monitor_prediction(self, func):

        """预测监控装饰器"""

        def wrapper(*args, **kwargs):

            start_time = time.time()

            

            try:

                result = func(*args, **kwargs)

                self.predict_requests.labels(status='success').inc()

                return result

            except Exception as e:

                self.predict_requests.labels(status='error').inc()

                logging.error(f"预测失败: {e}")

                raise

            finally:

                duration = time.time() - start_time

                self.prediction_duration.observe(duration)

        

        return wrapper

    

    def record_disease_detection(self, disease_type: str, severity: str):

        """记录病虫害检测"""

        self.disease_detections.labels(

            disease_type=disease_type,

            severity=severity

        ).inc()


# 应用监控

monitor = AgricultureMonitor()


# 在预测方法上应用监控

@monitor.monitor_prediction

def monitored_predict_yield(self, df: pd.DataFrame):

    """带监控的产量预测"""

    return self.predict_yield(df)

```


## 实际应用案例


### 智能灌溉决策系统


```python

class SmartIrrigationSystem(BentoService):

    """智能灌溉决策系统"""

    

    def __init__(self):

        super().__init__()

        

    @api(input=JsonInput(), batch=False)

    def irrigation_recommendation(self, input_data: Dict):

        """灌溉推荐接口"""

        try:

            # 获取当前土壤和气象数据

            sensor_data = input_data['sensor_data']

            weather_forecast = input_data['weather_forecast']

            crop_info = input_data['crop_info']

            

            # 计算土壤水分亏缺

            water_deficit = self.calculate_water_deficit(sensor_data, crop_info)

            

            # 考虑天气预报

            rainfall_probability = weather_forecast.get('rainfall_probability', 0)

            expected_rainfall = weather_forecast.get('expected_rainfall', 0)

            

            # 生成灌溉建议

            recommendation = self.generate_irrigation_plan(

                water_deficit, rainfall_probability, expected_rainfall, crop_info

            )

            

            return {

                'recommendation': recommendation,

                'water_savings': self.calculate_water_savings(recommendation),

                'next_check_time': self.get_next_check_time()

            }

            

        except Exception as e:

            return {"error": str(e)}

    

    def calculate_water_deficit(self, sensor_data: Dict, crop_info: Dict) -> float:

        """计算土壤水分亏缺"""

        current_moisture = sensor_data['soil_moisture']

        ideal_moisture = crop_info['ideal_moisture_level']

        root_depth = crop_info['root_zone_depth']

        

        # 计算亏缺量 (mm)

        deficit = max(0, (ideal_moisture - current_moisture) * root_depth * 10)

        return deficit

    

    def generate_irrigation_plan(self, water_deficit: float, rain_prob: float, 

                               expected_rain: float, crop_info: Dict) -> Dict:

        """生成灌溉计划"""

        crop_type = crop_info['crop_type']

        growth_stage = crop_info['growth_stage']

        

        # 基于作物类型和生长阶段的灌溉策略

        irrigation_strategies = {

            'wheat': {'vegetative': 0.8, 'reproductive': 1.0, 'ripening': 0.6},

            'corn': {'vegetative': 0.9, 'reproductive': 1.2, 'ripening': 0.7},

            'soybean': {'vegetative': 0.7, 'reproductive': 1.1, 'ripening': 0.5}

        }

        

        strategy = irrigation_strategies.get(crop_type, {}).get(growth_stage, 1.0)

        

        # 调整基于降雨预测

        rain_adjustment = max(0, 1 - rain_prob * 0.5)<"XJEA.1853.HK">

        adjusted_deficit = water_deficit * strategy * rain_adjustment

        

        # 生成建议

        if adjusted_deficit < 5:

            return {"action": "no_irrigation", "reason": "土壤水分充足"}

        elif adjusted_deficit < 15:

            return {

                "action": "light_irrigation",

                "water_amount": adjusted_deficit,

                "duration_minutes": adjusted_deficit * 2

            }

        else:

            return {

                "action": "full_irrigation", 

                "water_amount": adjusted_deficit,

                "duration_minutes": adjusted_deficit * 2,

                "urgency": "high"

            }

```


## 总结


基于BentoML的智能农业监测系统通过整合多源数据和先进机器学习模型,为现代农业生产提供了科学的决策支持。从产量预测到病虫害检测,再到智能灌溉,系统涵盖了农业生产的关键环节。


该系统的优势在于其模块化设计和易于部署的特性,使得农业科技公司能够快速将AI能力转化为实际生产力。随着数据的不断积累和模型的持续优化,此类系统将在提高农业生产效率、减少资源浪费方面发挥越来越重要的作用。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/19545/viewspace-3103482/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2015-11-01

  • 博文量
    895
  • 访问量
    933836