[[405478]] このチュートリアルでは、TensorFlow (Keras API) を使用して、データセット上のアラビア語の手書き文字を認識する必要があるマルチ分類タスク用のディープラーニング モデルを実装します。 データセットのダウンロードアドレス: https://www.kaggle.com/mloey1/ahcd1 データセットの紹介このデータセットは、19 歳から 40 歳までの 60 人の参加者が書いた 16,800 文字で構成されており、参加者の 90% は右利きです。 図7(a)と7(b)に示すように、各参加者は「alef」から「yeh」までの各文字を両方の形式で10回書きました。フォームは 300 dpi の解像度でスキャンされました。各ブロックは Matlab 2016a を使用して自動的にセグメント化され、各ブロックの座標が決定されました。データベースは、トレーニング セット (クラスごとに 13,440 文字、480 画像) とテスト セット (クラスごとに 3,360 文字、120 画像) の 2 つのセットに分かれています。データ ラベルの範囲は 1 ~ 28 カテゴリです。ここで、すべてのデータセットは CSV ファイルであり、画像のピクセル値とそれに対応するラベルを表し、対応する画像データは提供されません。 モジュールのインポート - numpyをnpとしてインポートする
- pandasをpdとしてインポートする
- # データフレームで display() の使用を許可します
- IPython.displayからdisplayをインポートする
- # 画像の読み込みと処理に必要なライブラリをインポートする
- csvをインポート
- PIL インポート画像から
- scipy.ndimageから回転をインポートする
データの読み取り - # トレーニングデータ画像
- letters_training_images_file_path = "../input/ahcd1/csvTrainImages 13440x1024.csv"
- # トレーニングデータラベル
- letters_training_labels_file_path = "../input/ahcd1/csvTrainLabel 13440x1.csv"
- # テストデータの画像とラベル
- letters_testing_images_file_path = "../input/ahcd1/csvTestImages 3360x1024.csv"
- letters_testing_labels_file_path = "../input/ahcd1/csvTestLabel 3360x1.csv"
-
- # データを読み込む
- training_letters_images = pd.read_csv(letters_training_images_file_path、ヘッダー=なし)
- training_letters_labels = pd.read_csv(letters_training_labels_file_path、ヘッダー=なし)
- テスト用文字画像 = pd.read_csv(文字画像ファイルパス、ヘッダー=なし)
- テスト文字ラベル = pd.read_csv(文字テストラベルファイルパス、ヘッダー=なし)
-
- print( "%d 32x32 ピクセルのトレーニング用アラビア文字画像。" %training_letters_images.shape[0])
- print( "%d 32x32 ピクセルのアラビア文字のテスト画像。" %testing_letters_images.shape[0])
- トレーニングレター画像.head()
アラビア文字の 32 x 32 ピクセルのトレーニング画像 13440 枚。アラビア文字の 32 x 32 ピクセルのテスト画像 3360 枚。 トレーニングデータの先頭を表示する - np.unique (トレーニング文字ラベル)
- 配列([ 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], dtype=int32)
次に、csv 値を画像に変換し、対応する画像のピクセル値を表示する必要があります。 - def convert_values_to_image(image_values, display= False ):
- image_array = np.asarray(image_values)
- image_array = image_array.reshape(32,32).astype( 'uint8' )
- # 元のデータセットは反転されているので、np.flip を使用して反転し、rotate を使用して回転させて、より良い画像を取得します。
- 画像配列 = np.flip(画像配列、0)
- image_array = 回転(image_array, -90)
- new_image = Image.fromarray(image_array)
- display == Trueの場合:
- 新しい画像を表示()
- new_imageを返す
- 値を画像に変換する(training_letters_images.loc[0], True )
これは文字fです。 次に、データの前処理、主に画像の正規化を実行します。画像内の各ピクセルを 255 で割り、[0, 1] に正規化することで、画像を再スケーリングします。 - トレーニングレターイメージのスケール = トレーニングレターイメージ.values.astype ( 'float32' ) /255
- トレーニング文字ラベル =トレーニング文字ラベル.値.astype ( 'int32' )
- テスト文字画像のスケール =テスト文字画像.値.astype ( 'float32' )/255
- テスト文字ラベル =テスト文字ラベル.値.astype ( 'int32' )
- print( "スケーリング後の文字のトレーニング画像" )
- 印刷(training_letters_images_scaled.shape)
- トレーニング文字画像スケール[0:5]
出力は次のようになります - スケーリング後の文字のトレーニング画像
- (13440, 1024)
ラベル csv ファイルから、これはマルチクラス分類問題であることがわかります。次のステップは、分類ラベルをエンコードすることです。カテゴリ ベクトルを行列型に変換することをお勧めします。 出力形式は次のとおりです: 1 ~ 28、0 ~ 27 のカテゴリに変換されます。 「alef」から「yeh」までの文字には、分類番号0から27が付けられています。 to_categoricalは、カテゴリベクトルをバイナリ(0と1のみ)の行列型表現に変換します。 ここでは、keras ワンホットエンコーディングを使用してこれらのカテゴリ値をエンコードします。 ワンホットエンコーディングは、整数をバイナリ行列に変換します。この配列には 1 つの「1」のみが含まれ、残りの要素は「0」になります。 - keras.utilsからto_categoricalをインポートする
-
- # ワンホットエンコーディング
- クラス数 = 28
-
- トレーニング文字ラベルエンコード = to_categorical(トレーニング文字ラベル-1、num_classes=クラス数)
- テスト文字ラベルエンコード = to_categorical(テスト文字ラベル-1、num_classes=クラス数)
- # (13440, 1024)
以下では、TensorFlow をバックエンドとして使用する場合、Keras CNN では、形状 (nb_samples、行、列、チャネル) を持つ 4D 配列を入力として必要とするため、入力画像を 32x32x1 に再形成します。 ここで、nb_samples は画像 (またはサンプル) の合計数に対応し、rows、columns、channels はそれぞれ画像あたりの行数、列数、チャネル数に対応します。 - # 入力文字画像を32x32x1に再形成します
- トレーニング文字画像のスケール = トレーニング文字画像のスケール.reshape([-1, 32, 32, 1])
- テスト文字画像のスケール = テスト文字画像のスケール.reshape([-1, 32, 32, 1])
-
- 印刷(トレーニング文字イメージのスケール.shape、トレーニング文字ラベルのエンコード.shape、テスト文字イメージのスケール.shape、テスト文字ラベルのエンコード.shape)
- # (13440, 32, 32, 1) (13440, 28) (3360, 32, 32, 1) (3360, 28)
したがって、画像は 32x32 ピクセルのグレースケール画像なので、入力画像を (nb_samples, 32, 32, 1) の形状の 4D テンソルに再形成します。 - #入力文字画像を32x32x1に再形成する
- トレーニング文字画像のスケール = トレーニング文字画像のスケール.reshape([-1, 32, 32, 1])
- テスト文字画像のスケール = テスト文字画像のスケール.reshape([-1, 32, 32, 1])
-
- 印刷(トレーニング文字イメージのスケール.shape、トレーニング文字ラベルのエンコード.shape、テスト文字イメージのスケール.shape、テスト文字ラベルのエンコード.shape)
設計モデル構造 - keras.modelsからSequentialをインポートする
- keras.layersからConv2D、MaxPooling2D、GlobalAveragePooling2D、BatchNormalization、Dropout、Dense をインポートします。
-
- def create_model(オプティマイザー= 'adam' 、カーネル初期化子= 'he_normal' 、アクティベーション= 'relu' ):
- #モデルを作成する
- モデル = シーケンシャル()
- モデルを追加します(Conv2D(filters=16, kernel_size=3, padding= 'same' , input_shape=(32, 32, 1), kernel_initializer=kernel_initializer, activation=activation))
- モデルを追加します(BatchNormalization())
- モデルを追加します(MaxPooling2D(pool_size=2))
- model.add(ドロップアウト(0.2))
-
- モデルを追加します(Conv2D(filters=32, kernel_size=3, padding= 'same' , kernel_initializer=kernel_initializer, activation=activation))
- モデルを追加します(BatchNormalization())
- モデルを追加します(MaxPooling2D(pool_size=2))
- model.add(ドロップアウト(0.2))
-
- モデルを追加します(Conv2D(filters=64, kernel_size=3, padding= 'same' , kernel_initializer=kernel_initializer, activation=activation))
- モデルを追加します(BatchNormalization())
- モデルを追加します(MaxPooling2D(pool_size=2))
- model.add(ドロップアウト(0.2))
-
- モデルを追加します(Conv2D(filters=128, kernel_size=3, padding= 'same' , kernel_initializer=kernel_initializer, activation=activation))
- モデルを追加します(BatchNormalization())
- モデルを追加します(MaxPooling2D(pool_size=2))
- model.add(ドロップアウト(0.2))
- モデルを追加します(GlobalAveragePooling2D())
-
- #完全に接続された最終層
- model.add (Dense(28, activation= 'softmax' ))
-
- # モデルをコンパイルする
- model.compile(損失= 'categorical_crossentropy' 、メトリック=[ 'accuracy' ]、オプティマイザー=オプティマイザー)
- リターンモデル
「モデル構造」- 最初の隠れ層は畳み込み層です。このレイヤーには、サイズが 3×3 の 16 個の特徴マップと relu という活性化関数があります。これは入力レイヤーであり、上記の構造を持つ画像が必要です。
- 2 番目のレイヤーはバッチ正規化レイヤーで、トレーニング データとテスト データの特徴分布の変化を解決します。BN レイヤーは、入力活性化関数の入力を正規化するために活性化関数の前に追加されます。これにより、入力データのオフセットと拡大の問題が解決されます。
- 3 番目のレイヤーは MaxPooling レイヤーです。最大プーリング層は入力をダウンサンプリングするために使用され、モデルが特徴について仮定を立てることを可能にし、過剰適合を減らします。また、学習するパラメータの数も減り、トレーニング時間も短縮されます。
- 次のレイヤーはドロップアウトを使用した正規化レイヤーです。過剰適合を減らすために、レイヤー内のニューロンの 20% をランダムに除外するように構成されています。
- 他の隠し層には、サイズ 3×3 の 32 個の特徴と、画像からより多くの特徴をキャプチャするための relu 活性化関数が含まれています。
- 他の隠れ層には64個と128個の特徴、サイズ3×3、およびrelu活性化関数が含まれています。
- 畳み込み層、MaxPooling、BatchNormalization、Regularization、および GlobalAveragePooling2D 層が 3 回繰り返されます。
- 最後の層は(出力クラスの数)を持つ出力層であり、複数のクラスがあるため、ソフトマックス活性化関数を使用します。各ニューロンはそのクラスの確率を与えます。
- これは多クラス分類問題であるため、カテゴリクロスエントロピーが損失関数として使用されます。ニューラル ネットワークのパフォーマンスを向上させるには、精度を指標として使用します。
- モデル = create_model(オプティマイザー = 'Adam' 、カーネル初期化子 = 'uniform' 、アクティベーション = 'relu' )
- モデル.要約()
「Keras は、Keras.utils.vis_utils モジュールでモデルの描画をサポートしています。このモジュールは、graphviz を使用して Keras モデルを描画するためのユーティリティ関数を提供します。」 - pydotをインポートする
- keras.utilsからplot_modelをインポートする
-
- plot_model(モデル、to_file= "model.png" 、show_shapes= True )
- IPython.displayからImage をIPythonImageとしてインポートします。
- 表示(IPythonImage( 'model.png' ))
モデルをトレーニングし、batch_size=20 を使用してモデルをトレーニングし、15 エポックにわたってモデルをトレーニングします。 - keras.callbacksからModelCheckpoint をインポートします
-
- # チェックポイントを使用して、後で使用するためにモデルの重みを保存します。
- チェックポインター = ModelCheckpoint(ファイルパス = 'weights.hdf5' 、詳細 = 1、save_best_only = True )
- history = model.fit(training_letters_images_scaled、training_letters_labels_encoded、validation_data=(testing_letters_images_scaled、testing_letters_labels_encoded)、epochs=15、batch_size=20、verbose=1、callbacks=[checkpointer])
トレーニング結果は次のとおりです。 最後に、Epochs は損失と精度の曲線をプロットします。 - matplotlib.pyplot をpltとしてインポートします。
-
- def plot_loss_accuracy(履歴):
- # 損失
- plt.figure(図サイズ=[8,6])
- plt.plot(history.history[ '損失' ], 'r' ,線幅=3.0)
- plt.plot(history.history[ 'val_loss' ], 'b' 、線幅=3.0)
- plt.legend([ 'トレーニング損失' , '検証損失' ],fontsize=18)
- plt.xlabel( 'エポック' 、フォントサイズ=16)
- plt.ylabel( '損失' 、フォントサイズ=16)
- plt.title( '損失曲線' 、フォントサイズ=16)
-
- # 正確さ
- plt.figure(図サイズ=[8,6])
- plt.plot(history.history[ '精度' ], 'r' ,線幅=3.0)
- plt.plot(history.history[ 'val_accuracy' ], 'b' 、線幅=3.0)
- plt.legend([ 'トレーニング精度' , '検証精度' ],fontsize=18)
- plt.xlabel( 'エポック' 、フォントサイズ=16)
- plt.ylabel( '精度' 、フォントサイズ=16)
- plt.title( '精度曲線' , fontsize=16)
-
- plot_loss_accuracy(履歴)
「最適な検証損失を持つモデルをロードする」- # 最適な検証損失を持つモデルをロードする
- モデルをロードして重み付けする( 'weights.hdf5' )
- メトリック = model.evaluate(testing_letters_images_scaled、testing_letters_labels_encoded、verbose=1)
- print( "テスト精度: {}" .format(metrics[1]))
- print( "テスト損失: {}" .format(metrics[0]))
出力は次のようになります。 - 3360/3360 [================================] - 0s 87us/ステップ
- テスト精度: 0.9678571224212646
- テスト損失: 0.11759862171020359
混同行列を印刷します。 - sklearn.metricsからclassification_reportをインポート
-
- def get_predicted_classes(モデル、データ、ラベル=なし):
- image_predictions = model.predict(データ)
- 予測クラス = np.argmax(画像予測、軸=1)
- true_classes = np.argmax(ラベル、軸=1)
- 予測クラス、真のクラス、画像予測を返す
-
- get_classification_report(y_true, y_pred): を定義します。
- 印刷(分類レポート(y_true, y_pred))
-
-
- y_pred、y_true、image_predictions = get_predicted_classes(モデル、testing_letters_images_scaled、testing_letters_labels_encoded)
- get_classification_report(y_true, y_pred)
出力は次のようになります。 - 精度再現率 F1スコア サポート
-
- 0 1.00 0.98 0.99 120
- 1 1.00 0.98 0.99 120
- 2 0.80 0.98 0.88 120
- 3 0.98 0.88 0.93 120
- 4 0.99 0.97 0.98 120
- 5 0.92 0.99 0.96 120
- 6 0.94 0.97 0.95 120
- 7 0.94 0.95 0.95 120
- 8 0.96 0.88 0.92 120
- 9 0.90 1.00 0.94 120
- 10 0.94 0.90 0.92 120
- 11 0.98 1.00 0.99 120
- 12 0.99 0.98 0.99 120
- 13 0.96 0.97 0.97 120
- 14 1.00 0.93 0.97 120
- 15 0.94 0.99 0.97 120
- 16 1.00 0.93 0.96 120
- 17 0.97 0.97 0.97 120
- 18 1.00 0.93 0.96 120
- 19 0.92 0.95 0.93 120
- 20 0.97 0.93 0.94 120
- 21 0.99 0.96 0.97 120
- 22 0.99 0.98 0.99 120
- 23 0.98 0.99 0.99 120
- 24 0.95 0.88 0.91 120
- 25 0.94 0.98 0.96 120
- 26 0.95 0.97 0.96 120
- 27 0.98 0.99 0.99 120
-
- 精度 0.96 3360
- マクロ平均0.96 0.96 0.96 3360
- 戦闘平均0.96 0.96 0.96 3360
最後に、関連する予測の絵をいくつかランダムに描きます - インデックス = np.random.randint(0, testing_letters_labels.shape[0],サイズ=49)
- y_pred = np.argmax(model.predict(training_letters_images_scaled)、軸=1)
-
- i , idx をenumerate(インデックス)で指定します:
- plt.サブプロット(7,7,i+1)
-
- image_array = トレーニング文字画像のスケール[idx][:,:,0]
- 画像配列 = np.flip(画像配列、0)
- image_array = rotate(image_array, -90)
-
- plt.imshow(image_array, cmap= 'グレー' )
- plt.title( "予測: {} - ラベル: {}" .format(y_pred[idx], (training_letters_labels[idx] -1)))
- plt.xticks([])
- plt.yticks([])
- plt.show()
|