背景
問題提起
目的
範囲
画像のロードと準備
画像のフォーマットと変換
特徴の抽出と選択
ロジスティック回帰
線形判別分析
K 最近隣者
ディシジョン ツリー
ランダムフォレスト
ナイーブ・ベイズ
サポートベクターマシン
精度とパフォーマンスの測定基準
機械学習モデルの比較
制限と課題
成果の概要
貢献と意義
今後の取り組みと改善
植物の病気は農業の生産性に重大な脅威をもたらし、農家に収量の損失と経済的困難をもたらします。植物の病気をタイムリーかつ正確に検出することは、効果的な病気管理戦略を実施し、作物への被害を最小限に抑えるために非常に重要です。従来の手作業による病気の診断方法は、時間がかかり、主観的であり、間違いが発生しやすいものです。したがって、機械学習や画像処理などのテクノロジーの統合が、植物病害検出を自動化および強化するための有望なアプローチとして浮上しています。
このプロジェクトの主な目的は、機械学習アルゴリズムと画像処理技術を使用して植物病気検出システムを開発することです。このシステムは、葉のデジタル画像を分析することによって、植物の葉を健康か病気かを正確に分類することを目的としています。検出プロセスを自動化することで、農家や農業専門家は植物の病気を迅速に特定して対処できるため、タイムリーな介入と作物管理の最適化が可能になります。
このプロジェクトの主な目的は次のとおりです
堅牢かつ正確な植物病害検出システムを開発する
植物の葉を自動分類するための機械学習アルゴリズムを実装する
画像処理技術を利用して、葉の画像から関連する特徴を抽出します。
さまざまな機械学習モデルのパフォーマンスと精度を評価する
システムとの簡単かつ直観的な対話のためのユーザーフレンドリーなインターフェイスを提供します
このプロジェクトは、特にリンゴの葉における植物の病気の検出に焦点を当てています。モデルのトレーニングとテストに使用されるデータセットは、Plant-Village データセットから取得されます。このデータセットには、健康なリンゴの葉と、リンゴ黒星病、黒腐病、スギリンゴさび病などの病気に罹患した葉の画像が含まれています。このシステムは、高精度を達成することを目指しています。病気を分類し、農家や農業専門家が植物の病気を効果的に特定して管理するための実用的なツールを提供します。このプロジェクトは、現場でのリアルタイムの疾病検出や、画像取得のためのハードウェア デバイスの統合についてはカバーしていません。
この植物病害検出システムに使用されるデータセットは、Plant-Village Dataset から取得したリンゴの葉の画像で構成されています。データセットは、リンゴの葉の状態のさまざまなクラスを表す 4 つの主要カテゴリ (Apple___Apple_scab、Apple___Black_rot、Apple___Cedar_apple_rust、および Apple___healthy) に編成されています。
Apple___Apple_scab: このカテゴリには 630 個の画像が含まれており、そのうち 598 個の画像がトレーニング用に割り当てられ、32 個の画像がテスト用に割り当てられています
Apple___Black_rot: このカテゴリにはデータセットに 621 枚の画像が含まれており、そのうち 589 枚の画像がトレーニング用に割り当てられ、32 枚の画像がテスト用に割り当てられています。
Apple___Cedar_apple_rust: データセットは、スギリンゴさび病の影響を受けた葉の 275 枚の画像で構成されており、261 枚の画像がトレーニングに使用され、14 枚の画像がテストに使用されます。
Apple___healthy: このカテゴリには、健康なリンゴの葉の画像が 1645 枚含まれています。これらのうち、1562 個の画像がトレーニング用に指定され、83 個の画像がテスト用に予約されています。
トレーニング画像は、パターンを認識し、健康な葉と病気の葉を区別するように機械学習モデルに教えるために利用されます。テスト画像は、目に見えないデータでトレーニングされたモデルのパフォーマンスと精度を評価するために使用されます。この多様なデータセットを活用することで、植物病検出システムは、リンゴの葉を健康な葉か、リンゴ黒星病、黒腐病、黒腐病などの病気に影響されているかを正確に分類することを目的としています。杉林檎のさび。データセットの構成により、システムは幅広い葉の状態から学習し、植物の病気を一般化して正確に特定する能力が向上します。
画像のロードと準備
リンゴの葉の病気検出プロジェクトのコンテキストでは、最初のステップは、さまざまな病気に罹患しているリンゴの葉の画像で構成されるデータセットを取得することです。これらの画像はシステムにロードされ、さらなる処理のためにアクセスできるようになります。さらに、画像は、一貫した解像度へのサイズ変更、不要な部分のトリミング、色分布の正規化など、必要な調整を実行して準備されます。 画像のフォーマットと変換 リンゴの葉の画像が読み込まれたら、フォーマットして変換する必要があります。プロジェクトの後続段階との互換性。これには、画像形式を JPEG や PNG などの特定のファイル タイプに変換することで標準化することが含まれます。さらに、一貫性を確保し、正確な分析を容易にするために、色空間の解像度やその他の画像属性を調整することもできます。
特徴の抽出と選択
特徴抽出は、リンゴの葉の病気を検出するための重要なステップです。葉の画像から関連する特徴を抽出するには、さまざまな手法が使用されます。これらの技術には、病気に関連するテクスチャパターンを捉えるためのテクスチャ分析、特定の病気に関連する変化を特定するための色検査、葉の形態の不規則性を検出するための形状の研究などが含まれます。これらの特徴を抽出することで、後続の機械学習アルゴリズムは、健康なリンゴの葉と病気のリンゴの葉を効果的に区別できるようになります。
機能の選択
このステップには、抽出された特徴の関連性と識別力に基づいてサブセットを選択することが含まれます。特徴選択は、ノイズや冗長な情報を排除することでデータセットの次元を削減するのに役立ちます。最も有益な特徴を選択することで、疾患検出モデルの効率と精度を向上させることができます。
リンゴの葉の病気検出プロジェクトでは、さまざまな機械学習アルゴリズムを利用して、効果的な病気分類モデルを開発しています。以下のアルゴリズムが採用されています
ロジスティック回帰: ロジスティック回帰は、抽出された特徴に基づいてリンゴの葉が健康であるか病気である確率を予測するために使用されます。
線形判別分析: 線形判別分析は、健康なサンプルと病気のサンプルを最もよく区別する特徴の線形組み合わせを見つけることで、リンゴの葉を分類するのに役立ちます。
K 最近傍法 (KNN): K 最近傍法は、リンゴの葉の特徴を特徴空間内の最近傍の特徴と比較することによって分類します。
デシジョン ツリー: デシジョン ツリーは、一連の if-else 条件を使用して、サンプルの特徴と階層関係に基づいてサンプルを分類します。
ランダム フォレスト: ランダム フォレストは、複数の決定木を組み合わせて分類精度を高めるアンサンブル学習方法です。
ナイーブ ベイズ: ナイーブ ベイズは、リンゴの葉が特定の病気クラスに属する確率を計算する確率的アルゴリズムです。
サポート ベクター マシン (SVM): サポート ベクター マシンは、高次元の特徴空間で超平面を構築し、リンゴの葉を分類します。
機械学習アルゴリズムを選択した後、対応する疾患ラベルを持つリンゴの葉の画像で構成されるラベル付きデータセットを使用してモデルがトレーニングされます。モデルは、このトレーニング段階で特徴と疾患クラスの間のパターンと関係を認識することを学習します。モデルの信頼性と一般化を確保するために、検証プロセスが実行されます。トレーニングされたモデルは、トレーニング中に使用されなかった別の検証データセットを使用して評価されます。これは、目に見えないリンゴの葉のサンプルを正確に分類するモデルの能力を評価するのに役立ちます
モデルがトレーニングされ検証されると、新しいまだ見たことのないリンゴの葉の画像を含む別のテスト データセットでテストされます。モデルは各サンプルの疾患クラスを予測し、精度、精度、再現率、F1 スコアなどのパフォーマンス評価指標を計算して、疾患検出におけるモデルの有効性を測定します。
[1] では:
# -----------------------------------# グローバル特徴抽出# --------- ------------------------from sklearn.preprocessing import LabelEncoderfrom sklearn.preprocessing import MinMaxScalerimport numpy as npimport mahotasimport cvimport osimport h5py# ----- ---------------# 調整可能なパラメータ# -----------------------------------images_per_class = 800fixed_size = tuple(( 500 , 500 ))train_path = "../dataset/train"test_path = "../dataset/test"h5_train_features = "../embeddings/features/features.h5"h5_train_labels = "../embeddings/labels/labels.h5 "bins = 8##### BGR To RGB ConversionIn [2]:# 各画像をBGR formatdef からの RGB rgb_bgr(image):rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)return rgb_img##### RGB から HSV (色相彩度値) ConversionIn [3]:# RGBdef bgr_hsv から HSV 画像形式への変換(rgb_img):hsv_img = cv2.cvtColor(rgb_img, cv2.COLOR_RGB2HSV)return hsv_img##### Image SegmentationIn [4]:# 緑と茶色の colordef を抽出 img_segmentation(rgb_img, hsv_img): lower_green = np.array([ 25 , 0 , 20 ])upper_green = np.array([ 100 , 255 , 255 ])healthy_mask = cv2.inRange(hsv_img, lower_green, upper_green)result = cv2.bitwise_and(rgb_img, rgb_img, Mask=healthy_mask) lower_brown = np.array([ 10 , 0 , 10 ])upper_brown = np.array([ 30 , 255 , 255 ])disease_mask = cv2.inRange(hsv_img, lower_brown, upper_brown)disease_result = cv2.bitwise_and(rgb_img, rgb_img, Mask=disease_mask)final_mask = health_mask + sick_maskfinal_result = cv2.bitwise_and(rgb_img, rgb_img, Mask=final_mask)return Final_result##### 特徴記述子の決定####### 1. Hu MomentsIn [5]:# feature-descriptor-1: Hu Momentsdef fd_hu_moments(image) :image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)feature = cv2.HuMoments(cv2.moments(image)). flatten()return feature###### 2.haralick TexturesIn [6]:# feature-descriptor-2:haralick Texturedef fd_haralick(image):gray = cv2.cvtColor (画像、cv2.COLOR_BGR2GRAY)haralick = mahotas.features.haralick(gray).mean(axis= 0 )return haralick###### 3. カラー ヒストグラムIn [7]:# 機能記述子-3: カラー ヒストグラムdef fd_histogram(image, マスク=なし):image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)hist = cv2 .calcHist( [イメージ]、[ 0 、 1 、 2 ]、なし、[ビン、ビン、ビン]、[ 0 、 256 、 0 、 256 、 0 、256 ] )cv2.normalize(hist, hist)return hist. flatten()##### トレーニング データセットのロードIn [8]:# トレーニング ラベルの取得train_labels = os.listdir(train_path)# トレーニング ラベルの並べ替えtrain_labels.sort() print(train_labels)# 特徴ベクトルとラベルを保持する空のリストglobal_features = []labels = [] ['Apple___Apple_scab', 'Apple___Black_rot', 'Apple___Cedar_apple_rust','Apple___healthy']##### データセットからの特徴とラベルの埋め込みの生成In [9]:# トレーニング データのサブフォルダーをループします。train_labels の training_name に対して:# joinトレーニングデータのパスと各種のトレーニングフォルダーimg_dir_path = os.path.join(train_path, training_name)# 現在のトレーニング label を取得しますcurrent_label = training_name# os.listdir(img_dir_path) の img の各サブフォルダー内の画像をループします:# 画像ファイル名を取得file = os.path.join( img_dir_path, img)# 画像を読み取り、固定サイズにサイズ変更しますimage = cv2.imread(file)image = cv2.resize(image, fix_size)# 関数をビットごとに実行RGB_BGR = rgb_bgr(image)BGR_HSV = bgr_hsv(RGB_BGR)IMG_SEGMENT = img_segmentation(RGB_BGR, BGR_HSV)# グローバル特徴記述子の呼び出しfv_hu_moments = fd_hu_moments(IMG_SEGMENT)fv_haralick = fd_haralick(IMG_SEGMENT)fv_histogram = fd_histogram(IMG_SEGMENT)# グローバル機能を連結global_feature = np.hstack([fv_histogram, fv_haralick,fv_hu_moments])# ラベルと機能のリストを更新しますVectorslabels.append(current_label)global_features.append(global_feature)print("[STATUS] 処理されたフォルダー: {}".format(current_label))print("[STATUS] グローバル特徴抽出が完了しました...") [STATUS] 処理済みフォルダー: Apple___Apple_scab[STATUS] 処理済みフォルダー: Apple___Black_rot[STATUS] 処理済みフォルダー: Apple___Cedar_apple_rust[STATUS] 処理済みフォルダー: Apple___healthy[STATUS] 完了しました グローバル特徴抽出...In [10]:# print(global_features)In [ 41]:# 全体的な特徴ベクトルを取得 sizeprint("[STATUS] 特徴ベクトルサイズ{}".format(np.array(global_features).shape)) [STATUS] 特徴ベクトル サイズ (3010, 532)In [12]:# トレーニング ラベル全体のサイズを取得# print(labels)print("[STATUS] トレーニング ラベル {}".format(np.array(labels).shape )) [ステータス] トレーニング ラベル (3010,)
ラベル エンコードされた値Apple___Apple_scab 0Apple___Black_rot 1Apple___Cedar_apple_rust 2Apple___healthy 3
[13] では:
targetNames = np.unique(labels) le = LabelEncoder() target = le.fit_transform(labels) print(targetNames) print("[STATUS] トレーニング ラベルがエンコードされました...") ['Apple___Apple_scab' 'Apple___Black_rot' 'Apple___Cedar_apple_rust' ' Apple___healthy'] [ステータス] トレーニング ラベルがエンコードされています...
[14] では:
from sklearn.preprocessing import MinMaxScalerscaler = MinMaxScaler(feature_range=( 0 , 1 ))rescaled_features =scaler.fit_transform(global_features)print("[STATUS] 特徴ベクトルが正規化されました...")rescaled_features[STATUS] 特徴ベクトルが正規化されました...Out [14]:array([[0.8974175 , 0.03450962、0.01845123、...、0.02027887、0.12693291、0.96573218]、 [0.89815922、0.13025558、0.02774864、...、0.02027767、0.12692423、0.96573354]、 [0.56777027, 0. , 0.01540143, ..., 0.02027886, 0.12693269,0.96573218], ...、 [0.95697685、0.01228793、0.00548476、...、0.02027886、0.12693346、0.96573218]、 [0.97704002、0.10614054、0.03136325、...、0.02027885、0.12692424、0.96573217]、 [0.95214074, 0.03819411, 0.03671892, ..., 0.02027886, 0.12692996,0.96573217]])print("[STATUS] ターゲット ラベル: {}".format(target))print("[STATUS] ターゲット ラベルの形状: {}".format(target.shape)) [STATUS] 対象ラベル: [0 0 0 ... 3 3 3] [ステータス] ターゲット ラベルの形状: (3010,)
a.特徴
h5f_data = h5py.File(h5_train_features, "w")h5f_data.create_dataset("dataset_1", data=np.array(rescaled_features))Out[16]:<HDF5 データセット "dataset_1": 形状 (3010, 532)、タイプ " <f8">
h5f_label = h5py.File(h5_train_labels, "w")h5f_label.create_dataset("dataset_1", data=np.array(target))Out[17]:<HDF5 データセット "dataset_1": 形状 (3010,)、タイプ "< i8">[43] 内:h5f_data.close()h5f_label.close()
# -----------------------------------# モデルのトレーニング# --------- ------------------------import h5pyimport numpy as npimport osimport cvimport warningsfrom matplotlib import pyplotfrom sklearn.model_selection import train_test_split,cross_val_scorefrom sklearn.model_selection import KFold, StratifiedKFoldfrom sklearn.metricsインポートconstruction_matrix、 Accuracy_score,classification_reportfrom sklearn.linear_model import LogisticRegressionfrom sklearn.tree import DecisionTreeClassifierfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.discriminant_anasis import LinearDiscriminantAnalysisfrom sklearn.naive_bayes import GaussianNBfrom sklearn.svm import SVCimport joblibwarnings.filterwarnings("ignore")# --------------------# 調整可能なパラメータ# ----------- --------num_trees = 100test_size = 0.seed = 9scoring = "accuracy"# トレーニング ラベルを取得train_labels = os.listdir(train_path)# トレーニング ラベルをソートtrain_labels.sort()if not os.path.exists(test_path):os.makedirs(test_path)# すべての機械学習モデルを作成models = []models.append(("LR", LogisticRegression(random_state=seed)))models.append(("LDA" 、LinearDiscriminantAnalysis()))models.append(("KNN", KNeighborsClassifier()))models.append(("DTC", DecisionTreeClassifier(random_state=seed)))models.append(("RF", RandomForestClassifier(n_estimators=num_trees,random_state=seed)))models.append(("NB ", GaussianNB()))models.append(("SVM", SVC(random_state=seed)))# 結果と名前を保持する変数results = []names = []# 特徴ベクトルと学習済みラベルをインポートsh5f_data = h5py.File(h5_train_features, "r")h5f_label = h5py.File(h5_train_labels, "r")global_features_string = h5f_data["dataset_1"]global_labels_string = h5f_label["dataset_1"]global_features = np.array(global_features_string)global_labels = np.array(global_labels_string)h5f_data.close()h5f_label.close()# 特徴ベクトルとラベルの形状を確認しますprint (「[ステータス] の機能形状: {}".format(global_features.shape))print("[ステータス] ラベル形状: {}".format(global_labels.shape))print("[ステータス] トレーニングが開始されました...")print(global_labels, len(global_labels)、len(global_features)) [STATUS] 特徴形状:(3010、532) [ステータス] ラベルの形状: (3010,) [ステータス] トレーニングが開始されました... [0 0 0 ... 3 3 3] 3010 3010
[38] では:
(trainDataGlobal,testDataGlobal,trainLabelsGlobal,testLabelsGlobal, ) = train_test_split(np.array(global_features), np.array(global_labels),test_size=test_size, random_state=seed)print("[ステータス] 分割されたトレイン データとテスト データ...")print("トレイン データ : {} ".format(trainDataGlobal.shape))print("テスト データ : {}".format(testDataGlobal.shape)) [ステータス] 分割されたトレイン データとテスト データ...トレイン データ : (2408, 532)テスト データ : (602, 532)In [40]:trainDataGlobalOut[40]:array([[9.47066972e-01, 1.97577832e-02 、5.34481987e-04、 ...、2.02788613e-02、1.26936845e-01、9.65732178e-01]、 [9.67673181e-01、4.20456024e-02、5.76285634e-02、...、2.02788294e-02、1.26933581e-01、9.65732217e-01]、 [9.84705756e-01、2.97800312e-02、1.34500344e-02、...、2.02788553e-02、1.26941878e-01、9.65732187e-01]、 ...、 [8.64347882e-01、5.89053245e-02、4.27430333e-02、...、2.02791643e-02、1.26961451e-01、9.65733689e-01]、 [9.85818416e-01、1.47428536e-03、3.35008392e-03、...、2.02767694e-02、1.26792776e-01、9.65732951e-01]、 [9.93152188e-01、1.31020292e-03、8.50637768e-04、...、2.02910354e-02、1.27475382e-01、9.65721108e-01]])
[22] では:
名前の場合、モデル内のモデル:kfold = KFold(n_splits= 10 )cv_results =cross_val_score(model, trainDataGlobal, trainLabelsGlobal, cv=kfold,scoring=scoring)results.append(cv_results)names.append(name)msg = "%s : %f (%f)" % (名前, cv_results.mean()、cv_results.std())print(msg)LR: 0.900346 (0.020452)LDA: 0.892038 (0.017931)KNN: 0.884978 (0.019588)CART: 0.886210 (0.014771)RF: 0.967191 (0.012676)NB: 0.839293 (0.014065)SVM: 0.885813 (0.021190)
[23] では:
fig = pyplot.figure()fig.suptitle("機械学習アルゴリズムの比較")ax = fig.add_subplot( 111 )pyplot.boxplot(results)ax.set_xticklabels(names)pyplot.show()
上記の結果から、ランダム フォレスト分類子モデルの精度が 96.7% と最も高く、ガウス NB モデルの精度が最も低い 83.9% であることがわかります。
<div class="highlight highlight-source-python notranslateposition-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content=" [24] 内: clf = RandomForestClassifier(n_estimators=num_trees, random_state=シード) [25] 内: clf.fit(trainDataGlobal, trainLabelsGlobal) len(trainDataGlobal)、 len(trainLabelsGlobal) Out[25]: (2408, 2408) In [34]: y_predict = clf.predict(testDataGlobal) testLabelsGlobal Out[34]: array([3, 3, 1, 3, 0, 3, 1, 1、2、1、1、0、1、3、3、3、3、 2、0、2、0、3、3、3、3、3、3、3、1、3、1、1、3、3、1、3、3、3、2、3、1、3、 3、3、1、0、0、3、1、3、3、0、3、3、2、3、 0、3、1、0、3、0、3、3、1、3、3、0、3、3、0、3、3、3、3、2、1、1、2、0、2、 1、1、0、0、3、2、0、3、2、3、3、2、3、3、 1、1、3、2、0、2、1、1、2、3、3、3、1、1、0、3、0、3、3、0、3、3、3、1、2、 3、2、3、0、3、0、3、1、3、3、3、3、3、2、 1、0、1、3、3、3、1、3、3、0、0、3、3、3、0、2、3、3、0、1、1、3、0、0、3、 1、3、3、1、3、2、1、0、0、3、0、1、0、1、 0、1、2、3、3、3、3、3、2、1、1、3、3、1、3、1、3、2、1、3、3、0、3、0、3、 3、3、1、1、3、2、3、0、3、3、3、0、3、3、 3、3、3、3、3、3、0、3、3、1、1、0、3、0、3、1、3、3、3、1、3、3、0、3、0、 2、3、3、0、3、3、3、3、0、2、3、1、3、3、 3、0、3、1、3、3、3、1、3、0、2、0、3、3、3、2、3、3、3、0、0、1、1、3、3、 0、3、2、0、1、1、3、0、3、1、1、3、2、2、 2、3、3、3、1、1、3、0、3、0、1、3、3、0、3、1、0、3、0、3、3、2、3、3、3、 3、0、3、3、3、1、0、3、2、1、3、0、1、1、 0、1、3、2、0、3、0、3、1、0、3、2、0、3、0、0、2、1、3、0、3、3、0、0、3、 3、1、3、0、3、3、3、3、0、0、3、3、3、3、 3、3、1、1、3、3、0、3、3、3、1、0、1、3、0、1、3、0、3、3、3、0、3、3、0、 1、3、3、1、3、0、3、0、3、3、3、3、3、3、 1、0、3、3、0、3、3、1、0、3、1、1、3、3、3、2、3、0、0、3、3、3、3、3、3、 2、1、3、3、0、3、0、1、3、1、3、3、1、1、 1、1、3、3、3、1、3、0、3、3、3、2、3、1、3、3、1、1、3、3、3、0、0、3、3、 0、3、3、0、0、3、3、3、3、3、1、1、0、3、 3、3、3、0、3、1