Press "Enter" to skip to content

使用机器学习和Flask部署的农作物产量预测

介绍

农作物产量预测是农业行业中必不可少的预测性分析技术。它是一种农业实践,可以帮助农民和农业企业预测特定季节的农作物产量,以便更好地种植和收获。预测性分析是农业行业中可用于农作物产量预测、风险缓解、降低肥料成本等方面的有力工具。使用机器学习和 Flask 部署的农作物产量预测将对天气条件、土壤质量、果实结数、果实质量等进行分析。

Unsplash

学习目标

  • 我们将简要介绍使用授粉模拟建模来预测农作物产量的端到端项目。
  • 我们将遵循数据科学项目生命周期的每个步骤,包括数据探索、预处理、建模、评估和部署。
  • 最后,我们将使用 Flask API 在名为 render 的云服务平台上部署模型。

因此,让我们开始这个激动人心的实际问题声明。

本文是数据科学博客马拉松的一部分。

项目描述

用于此项目的数据集是使用空间显式模拟计算模型生成的,分析和研究影响野生蓝莓预测的各种因素,包括:

  • 植物空间排列
  • 异交和自交
  • 蜜蜂物种组成
  • 天气条件(单独和组合)对野生蓝莓的授粉效率和产量的影响。

该模拟模型已通过在过去30年中在美国缅因州和加拿大海岸收集的田野观察和实验数据进行验证,并现在是一个有用的工具,用于假设测试和野生蓝莓产量预测的估计。这个模拟数据为研究人员提供了从实地收集的实际数据,用于各种农作物产量预测实验,同时为开发人员和数据科学家提供了构建用于农作物产量预测的真实世界机器学习模型的数据。

A simulated wild blueberry field

什么是授粉模拟模型?

授粉模拟建模是使用计算机模型模拟授粉过程的过程。授粉模拟有各种用途,例如:

  • 研究不同因素对授粉的影响,例如气候变化、栖息地丧失和农药
  • 设计授粉友好的景观
  • 预测授粉对农作物产量的影响

授粉模拟模型可用于研究花粉粒在花之间的移动、授粉事件的时间以及不同授粉策略的有效性。这些信息可用于提高授粉率和农作物产量,进一步帮助农民有效地生产农作物并获得最佳产量。

授粉模拟模型仍在开发中,但它们有潜力在农业的未来扮演重要角色。通过了解授粉的工作原理,我们可以更好地保护和管理这个重要的过程。

在我们的项目中,我们将使用一个包含各种特征的数据集,例如“ clonesize ”、“ honeybee ”、“ RainingDays ”、“ AverageRainingDays ”等,这些特征是使用授粉模拟过程创建的,以估算农作物产量。

问题陈述

在此项目中,我们的任务是根据其他17个特征逐步对产量变量(目标特征)进行分类,每天都要经过每个任务。评估指标将是 RMSE 评分。我们将使用 Python 的 Flask 框架在云平台上部署模型。

先决条件

此项目非常适合中级数据科学和机器学习学习者构建自己的投资组合项目。如果初学者熟悉以下技能,则可以从事此项目:

  • 掌握 Python 编程语言、使用 scikit-learn 库的机器学习算法的知识
  • 基本了解使用 Python 的 Flask 框架开发网站
  • 理解回归评估指标

数据描述

在这一部分中,我们将查看数据集中每一个变量,为我们的项目做好准备。

  • 克隆大小 — m2 — 田地中蓝莓克隆体的平均大小
  • 蜜蜂 — 只蜂/m2/min — 田地中蜜蜂的密度
  • 大黄蜂 — 只蜂/m2/min — 田地中大黄蜂的密度
  • 安德烈蜜蜂 — 只蜂/m2/min — 田地中安德烈蜜蜂的密度
  • 短角蜂 — 只蜂/m2/min — 田地中短角蜂的密度
  • 上层温度范围最高值 — ℃ —开花季节每天上层带的最高气温记录
  • 上层温度范围最低值 — ℃ — 上层带每日气温的最低记录
  • 上层温度范围平均值 — ℃ — 上层带每日气温的平均值
  • 下层温度范围最高值 — ℃ — 下层带每日气温的最高记录
  • 下层温度范围最低值 — ℃ — 下层带每日气温的最低记录
  • 下层温度范围平均值 — ℃ — 下层带每日气温的平均值
  • 降雨天数 — 天 — 在开花季节中,每一天的降雨量都大于零
  • 降雨天数平均值 — 天 — 整个开花季节下雨天的平均值
  • 果实成熟时间 — 果实成熟的时间
  • 果实质量 — 果实的质量
  • 种子数量 — 果实中种子的数量
  • 产量 — 庄稼产量(目标变量)

这些数据对于作物预测有什么价值?

  • 这个数据集提供了有关野生蓝莓植物空间特征、蜜蜂物种和天气状况的实用信息。因此,它使研究人员和开发人员能够构建机器学习模型,早期预测蓝莓产量。
  • 这个数据集对于其他研究人员来说非常重要,他们拥有现场观测数据,但希望通过比较使用真实数据与使用计算机模拟生成的数据作为产量预测输入来测试和评估不同机器学习算法的性能。
  • 不同层次的教育工作者可以使用数据集来培训农业行业中的机器学习分类或回归问题。

加载数据集

在这一部分中,我们将在您所在的任何环境中加载数据集。在kaggle环境中加载数据集。使用kaggle数据集或将其下载到本地计算机并在本地环境中运行。

数据集来源:点击这里

让我们看看如何加载数据集并为项目加载库的代码。

import numpy as np # 线性代数
import pandas as pd # 数据处理,CSV文件输入/输出(例如pd.read_csv)
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.feature_selection import mutual_info_regression, SelectKBest
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.model_selection import train_test_split, cross_val_score, KFold 
from sklearn.model_selection import GridSearchCV, RepeatedKFold
from sklearn.ensemble import AdaBoostRegressor, GradientBoostingRegressor 
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
import sklearn
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
import statsmodels.api as sm
from xgboost import XGBRegressor
import shap

# 在kaggle中设置操作系统环境
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# 读取CSV文件并在平台上加载前5行 
df = pd.read_csv("/kaggle/input/wildblueberrydatasetpollinationsimulation/
WildBlueberryPollinationSimulationData.csv", 
                 index_col='Row#')
df.head()
以上代码的输出
# 打印数据集的元数据
df.info()

# 数据描述
df.describe()
以上代码的输出
代码的输出

以上代码像‘df.info()’提供数据框的摘要信息,如行数、空值数、每个变量的数据类型等,而‘df.describe()’提供数据集的描述性统计信息,如数据集中每个变量的均值、中位数、计数和百分位数等。

探索性数据分析

在本节中,我们将研究作物数据集的探索性数据分析并从数据集中得出见解。

数据集的热力图

# 从数据集中创建特征集和目标变量
features_df = df.drop('yield', axis=1)
tar = df['yield']

# 绘制数据集的热力图
plt.figure(figsize=(15,15))
sns.heatmap(df.corr(), annot=True, vmin=-1, vmax=1)
plt.show()
代码的输出

上图显示了数据集的相关系数的可视化。使用 Python 的 Seaborn 库,我们可以在仅三行代码中可视化它。

目标变量的分布

# 使用 seaborn 库绘制目标变量 'yield' 的箱线图
plt.figure(figsize=(5,5))
sns.boxplot(x='yield', data=df)
plt.show()
代码的输出

上述代码使用箱线图显示了目标变量的分布。我们可以看到分布的中位数约为 6000,有几个产量最低的异常值。

数据集的分类特征分布

# matplotlib 子图技巧绘制分类特征的箱线图
nominal_df = df[['MaxOfUpperTRange','MinOfUpperTRange','AverageOfUpperTRange','MaxOfLowerTRange',
               'MinOfLowerTRange','AverageOfLowerTRange','RainingDays','AverageRainingDays']]

fig, ax = plt.subplots(2,4, figsize=(20,13))
for e, col in enumerate(nominal_df.columns):
    if e<=3:
        sns.boxplot(data=df, x=col, y='yield', ax=ax[0,e])
    else:
        sns.boxplot(data=df, x=col, y='yield', ax=ax[1,e-4])       
plt.show()
输出

数据集中蜜蜂类型的分布

# matplotlib 子图技巧绘制我们数据集中蜜蜂的分布
plt.figure(figsize=(15,10))
plt.subplot(2,3,1)
plt.hist(df['bumbles'])
plt.title("bumbles 列的直方图")
plt.subplot(2,3,2)
plt.hist(df['andrena'])
plt.title("andrena 列的直方图")
plt.subplot(2,3,3)
plt.hist(df['osmia'])
plt.title("osmia 列的直方图")
plt.subplot(2,3,4)
plt.hist(df['clonesize'])
plt.title("clonesize 列的直方图")
plt.subplot(2,3,5)
plt.hist(df['honeybee'])
plt.title("honeybee 列的直方图")
plt.show()
输出

让我们记录一些关于分析的观察结果:

  • 上下T范围列彼此相关
  • 雨天和平均雨天相关
  • 果实质量 ‘,’ 果实结实度 ‘和’ 种子数 ‘是相关的
  • ‘大黄蜂’列高度不平衡,而’安德瑞纳’和’osmia’列则不是
  • ‘蜜蜂’与’克隆大小’相比也是不平衡的列

数据预处理和数据准备

在本节中,我们将为建模预处理数据集。我们将执行“互信息回归”以从数据集中选择最佳特征,对我们数据集中的蜜蜂类型进行聚类,并对数据集进行标准化,以进行高效的机器学习建模。

互信息回归

# 运行数据集的MI分数
mi_score = mutual_info_regression(features_df, tar, n_neighbors=3,random_state=42)
mi_score_df = pd.DataFrame({'columns':features_df.columns, 'MI_score':mi_score})
mi_score_df.sort_values(by='MI_score', ascending=False)
上述代码的输出

上述代码使用Pearson系数计算互相关回归,以找到与目标变量最相关的特征。我们可以看到按降序排列的最相关特征以及与目标特征最相关的特征。现在,我们将对蜜蜂类型进行聚类,以创建新特征。

K-means聚类

# 使用kmeans算法进行聚类
X_clus = features_df[['honeybee','osmia','bumbles','andrena']]

# 使用标准缩放器标准化数据集
scaler = StandardScaler()
scaler.fit(X_clus)
X_new_clus = scaler.transform(X_clus)

# K均值聚类
clustering = KMeans(n_clusters=3, random_state=42)
clustering.fit(X_new_clus)
n_cluster = clustering.labels_

# 将新特征添加到feature_Df中
features_df['n_cluster'] = n_cluster
df['n_cluster'] = n_cluster
features_df['n_cluster'].value_counts()

---------------------------------[输出]----------------------------------
1    368
0    213
2    196
Name: n_cluster, dtype: int64

上述代码将标准化数据集,然后应用聚类算法将行分组为3个不同的组。

使用Min-Max缩放器的数据归一化

features_set = ['AverageRainingDays','clonesize','AverageOfLowerTRange',
               'AverageOfUpperTRange','honeybee','osmia','bumbles','andrena','n_cluster']

# 最终数据框架 
X = features_df[features_set]
y = tar.round(1)

# 使用缩放器训练和测试数据集来通过缩放数据集构建基线模型使用GBT和RFs
mx_scaler = MinMaxScaler()
X_scaled = pd.DataFrame(mx_scaler.fit_transform(X))
X_scaled.columns = X.columns

上述代码表示规范化特征集’ X_scaled ‘和目标变量’ y ‘,这将用于建模。

建模和评估

在本节中,我们将查看使用梯度提升建模和超参数调整进行的机器学习建模,以获得所需的模型准确度和性能。此外,使用statsmodels库和形状模型解释器查看普通最小二乘回归建模,以可视化哪些特征对于我们的目标作物收成预测最重要。

机器学习建模基线

# 让我们将数据拟合到像Adaboost、GradientBoost和随机森林这样的模型中
model_dict = {"abr": AdaBoostRegressor(), 
              "gbr": GradientBoostingRegressor(), 
              "rfr": RandomForestRegressor()
             }

# 模型的交叉值分数
for key, val in model_dict.items():
    print(f"{key}的交叉验证")
    score = cross_val_score(val, X_scaled, y, cv=5, scoring='neg_mean_squared_error')
    mean_score = -np.sum(score)/5
    sqrt_score = np.sqrt(mean_score) 
    print(sqrt_score)

-----------------------------------[输出]------------------------------------
abr的交叉验证
730.974385377955
gbr的交叉验证
528.1673164806733
rfr的交叉验证
608.0681265123212

在上述机器学习建模中,我们发现梯度提升回归模型的均方误差最低,而Adaboost回归模型的误差最高。现在,我们将训练梯度提升模型,并使用scikit-learn训练和测试拆分方法评估误差。

# 拆分训练和测试数据
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# 梯度提升回归建模
bgt = GradientBoostingRegressor(random_state=42)
bgt.fit(X_train,y_train)
preds = bgt.predict(X_test)
score = bgt.score(X_train,y_train)
rmse_score = np.sqrt(mean_squared_error(y_test, preds))
r2_score = r2_score(y_test, preds)
print("梯度提升机器的RMSE分数:", rmse_score)      
print("模型的R2分数:", r2_score)

-----------------------------[输出]-------------------------------------------
梯度提升机器的RMSE分数: 363.18286194620714
模型的R2分数: 0.9321362721127562

在这里,我们可以看到梯度提升建模的RMSE分数,没有调整模型的超参数,约为363。而模型的R2分数约为93%,比基线准确性更好。进一步调整超参数以优化机器学习模型的准确性。

超参数调整

# K-fold拆分数据集
kf = KFold(n_splits = 5, shuffle=True, random_state=0)

# 调整超参数的params grid
param_grid = {'n_estimators': [100,200,400,500,800],
             'learning_rate': [0.1,0.05,0.3,0.7],
             'min_samples_split': [2,4],
             'min_samples_leaf': [0.1,0.4],
             'max_depth': [3,4,7]
             }

# GBR估计器对象
estimator = GradientBoostingRegressor(random_state=42)

# 网格搜索CV对象
clf = GridSearchCV(estimator=estimator, param_grid=param_grid, cv=kf, 
                   scoring='neg_mean_squared_error', n_jobs=-1)
clf.fit(X_scaled,y)

# 打印最佳估计器和参数
best_estim = clf.best_estimator_
best_score = clf.best_score_
best_param = clf.best_params_
print("最佳估计器:", best_estim)
print("最佳分数:", np.sqrt(-best_score))

-----------------------------------[输出]----------------------------------
最佳估计器: GradientBoostingRegressor(max_depth=7, min_samples_leaf=0.1, 
                                          n_estimators=500, random_state=42)
最佳分数: 306.57274619213206

我们可以看到,调整后的梯度提升模型的误差进一步降低,我们已经为我们的ML模型优化了参数。

Shap模型解释器

机器学习可解释性是目前ML建模中非常重要的一个方面。虽然ML模型在许多领域取得了很好的成果,但它们固有的复杂性使得理解它们如何到达某些预测或决策变得具有挑战性。Shap库使用“shaply”值来衡量哪些特征是预测目标值的影响者。现在让我们看看我们的梯度提升模型的“shap”模型解释器图。

# shaply树解释器
shap_tree = shap.TreeExplainer(bgt)
shap_values = shap_tree.shap_values(X_test)
shap.summary_plot(shap_values, X_test)
上述代码的输出

从上面的输出图可以清楚地看出,AverageRainingDays变量是解释目标变量预测值的最有影响力的变量,而andrena功能对预测变量的结果影响最小。

使用FlaskAPI部署模型

在本节中,我们将使用FlaskAPI在名为render.com的云服务平台上部署机器学习模型。在部署之前,有必要将模型文件保存为joblib扩展名,以便创建可以在云上部署的API。

保存模型文件

# remove the 'n_cluster' feature from the dataset
X_train_n = X_train.drop('n_cluster', axis=1)
X_test_n = X_test.drop('n_cluster', axis=1)

# train a model for flask API creation =
xgb_model = XGBRegressor(max_depth=9, min_child_weight=7, subsample=1.0)
xgb_model.fit(X_train_n, y_train)
pr = xgb_model.predict(X_test_n)
err = mean_absolute_error(y_test, pr)
rmse_n = np.sqrt(mean_squared_error(y_test, pr))

# after training, save the model using joblib library
joblib.dump(xgb_model, 'wbb_xgb_model2.joblib')

如上所示,我们已经在上面的代码中保存了模型文件,以及如何编写Flask应用程序文件和模型文件以上传到github存储库。

应用程序存储库结构

应用程序存储库的屏幕截图

上面的图像是包含以下文件和目录的应用程序存储库的快照。

  • app.py — Flask应用程序文件
  • model.py — 模型预测文件
  • requirements.txt — 应用程序依赖项
  • Model directory — 已保存的模型文件
  • templates directory — 前端UI文件

app.py文件

from flask import Flask, render_template, Response
from flask_restful import reqparse, Api
import flask

import numpy as np
import pandas as pd
import ast

import os
import json

from model import predict_yield

curr_path = os.path.dirname(os.path.realpath(__file__))

feature_cols = ['AverageRainingDays', 'clonesize', 'AverageOfLowerTRange',
    'AverageOfUpperTRange', 'honeybee', 'osmia', 'bumbles', 'andrena']

context_dict = {
    'feats': feature_cols,
    'zip': zip,
    'range': range,
    'len': len,
    'list': list,
}

app = Flask(__name__)
api = Api(app)

# # FOR FORM PARSING
parser = reqparse.RequestParser()
parser.add_argument('list', type=list)

@app.route('/api/predict', methods=['GET','POST'])
def api_predict():
    data = flask.request.form.get('single input')
    
    # converts json to int 
    i = ast.literal_eval(data)
    
    y_pred = predict_yield(np.array(i).reshape(1,-1))
    
    return {'message':"success", "pred":json.dumps(int(y_pred))}

@app.route('/')
def index():
    
    # render the index.html templete
    
    return render_template("index.html", **context_dict)

@app.route('/predict', methods=['POST'])
def predict():
    # flask.request.form.keys() will print all the input from form
    test_data = []
    for val in flask.request.form.values():
        test_data.append(float(val))
    test_data = np.array(test_data).reshape(1,-1)

    y_pred = predict_yield(test_data)
    context_dict['pred']= y_pred

    print(y_pred)

    return render_template('index.html', **context_dict)

if __name__ == "__main__":
    app.run()

以上代码是Python文件,它从用户那里获取输入并在前端打印出农作物产量预测结果。

Model.py 文件

import joblib 
import pandas as pd
import numpy as np
import os

# 加载模型文件
curr_path = os.path.dirname(os.path.realpath(__file__))
xgb_model = joblib.load(curr_path + "/model/wbb_xgb_model2.joblib")

# 预测农作物产量的函数
def predict_yield(attributes: np.ndarray):
    """ 返回蓝莓产量值 """
    # print(attributes.shape) # (1,8)

    pred = xgb_model.predict(attributes)
    print("预测产量")

    return pred[0]
    

Model.py 文件在运行时加载模型并输出预测结果。

在 Render 上部署

一旦所有文件都被推送到 GitHub 存储库,您只需在 render.com 上创建一个帐户,然后将包含 app.py 文件以及其他工件的 repo 分支推送到 deploy,只需几秒钟即可完成。此外,render 还提供了自动部署选项,确保您对部署文件所做的任何更改都会自动反映在网站上。

Screenshot of the render cloud deployment process

您可以在此 GitHub 存储库链接中找到有关该项目和代码的更多信息。

结论

在本文中,我们学习了使用机器学习算法预测野生蓝莓产量的端到端项目,并使用 FlaskAPI 进行部署。我们开始加载数据集,然后进行探索性数据分析、数据预处理、机器学习建模和云服务平台上的部署。

结果显示,该模型能够预测作物产量的 R2 值高达 93%。Flask API 可以轻松访问模型并用于进行预测。它使得广大用户,包括农民、研究人员和政策制定者,都能够使用。现在,让我们看一下本文中学到的一些经验教训。

  1. 我们学习了如何为项目定义问题陈述并执行端到端的 ML 项目流程。
  2. 我们学习了有关探索性数据分析和数据集预处理的知识,以进行建模。
  3. 最后,我们将机器学习算法应用于特征集,以部署预测模型。

常见问题

本文中显示的媒体不是 Analytics Vidhya 拥有的,而是根据作者的判断使用的。

Leave a Reply

Your email address will not be published. Required fields are marked *