この記事では、自作のスイカ データセットをディープ ラーニング モデルのトレーニングに使用することを目的としており、
スイカを手で叩く音声を分析し、高速フーリエ変換を実行して周波数領域の特徴を抽出し、モデルには 1 次元畳み込みニューラル ネットワーク モデルを使用します。スイカの熟度検出モデルのトレーニングと構築。
目次
1. データセットの前処理
1. データ収集
モデルの学習に使用する音声データセットは自分で購入して録音したもので
、音声を収集する際、スイカの叩き方は「叩く(P)」「遊ぶ(T)」「ノックする(Q)」の3つに分類されます。
スイカに関する情報:
スイカの音声は合計 939 個収集されています。
データ セットには合計 158 個のスイカ サンプルが含まれており、各スイカには複数の音声データが含まれています。86
個のスイカ サンプルがトレーニング セット、36 個のテスト セット、および 36 個の検証セットとして選択されています。
基本データの比率は 6:2:2 です。
スイカ | ミディアムレア | 成熟した | 熟れすぎた | 部分的に生まれた | 和 |
---|---|---|---|---|---|
スイカの数 | 25 | 76 | 31 | 26 | 158 |
音声の数 | 145 | 440 | 192 | 162 | 939 |
ラベル | 0 | 1 | 2 | 3 |
2. データの前処理
自己収集されたデータ セットのため、各音声の長さは 6 ~ 30 秒であり、複数のノック信号が含まれているため、データを前処理する必要があります。
2.1 エンドポイントの検出
エンドポイント検出の詳細なリンク: matlab を使用した二重しきい値法を使用したエンドポイント検出
エンドポイント検出:音声を含む信号から音声の開始点と終了点を判定することを意味する
意義:サンプル数を増やすだけでなく、ネットワーク学習時の不要な計算を削減し、モデル学習の精度を向上させることができる。
デュアルパラメータデュアル閾値エンドポイント検出レンダリング
図から、終点検出処理後、サンプル数が増加し、信号長が大幅に短縮されていることがわかります。
1. オーディオ時間が短すぎないようにする必要があります。サンプリング
周波数が 16kHz で、各データ サンプルが信号を表すために周波数領域信号の最初の 1000 データを抽出する必要があると仮定すると、次のことを確認する必要があります。傍受された各音声の持続時間は少なくとも 0.0625 秒です (もちろん、傍受された各音声には少なくとも 1 つの励起信号が含まれています)。
サンプリング周波数=単位時間当たりのサンプリング点数=サンプリング点数/サンプリング時間
2. しきい値の設定は適切である必要があります。
音声が多すぎるため、バッチ処理が必要です。バッチ処理の前に、しきい値の設定は適切でなければなりません。
単一パラメータの二重しきい値方法のみが使用されると仮定します。短期エネルギー法に関するエンドポイント検出では、励起信号が正常に検出できるように、オーディオ (またはその他のパラメーター) の短期平均エネルギーに基づいてしきい値 (amp1 および amp2) が設定されます。
しきい値を適切に設定して、有効な励起信号を見つけます
2.2 データの強化
オーディオのエンドポイント検出後にデータ量が十分でない場合は、他のデータ拡張を実行できます (エンドポイント検出もデータ拡張とみなされます)。
データ拡張を実行するときは、拡張データとソース データの間に小さな違いが生じるように、いくつかの小さな変更のみを加えることが最善です。元のデータの構造を変更しないように注意してください。変更しないと、「ダーティ データ」が生成されます。データ処理により、オーディオ データの拡張により、モデルの過学習を回避し、より一般的なモデルにすることができます。
オーディオに加えられた変更は次のとおりです: ノイズの追加、波形のストレッチ、および高音の補正。
追加されたノイズ
追加されたノイズは、平均値 0、標準偏差 1 のガウス ホワイト ノイズです。
#####增加噪声#####
def add_noise(data):
# 0.02为噪声因子
wn = np.random.normal(0, 1, len(data))
return np.where(data != 0.0, data.astype('float64') + 0.02 * wn, 0.0).astype(np.float32)
波形ストレッチは、
ピッチに影響を与えることなく、サウンドの速度/持続時間を変更します。
#####波形拉伸#####
def time_stretch(x, rate):
# rate:拉伸的尺寸,
# rate > 1 加快速度
# rate < 1 放慢速度
return librosa.effects.time_stretch(x, rate)
高音補正
ピッチ補正は、音の速度には影響せずにピッチのみを変更します。
#####音高修正#####
def pitch_shifting(x, sr, n_steps, bins_per_octave=12):
# sr: 音频采样率
# n_steps: 要移动多少步
# bins_per_octave: 每个八度音阶(半音)多少步
return librosa.effects.pitch_shift(x, sr, n_steps, bins_per_octave=bins_per_octave)
例
import librosa
import numpy as np
import matplotlib.pyplot as plt
import soundfile as sf
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示符号
fs = 16000
wav_data, y = librosa.load(r"C:\Users\Administrator\Desktop\test\10_28_1190001.wav", sr=fs, mono=True)
#####1.增加噪声#####
def add_noise(data):
# 0.02为噪声因子
wn = np.random.normal(0, 1, len(data))
return np.where(data != 0.0, data.astype('float64') + 0.02 * wn, 0.0).astype(np.float32)
#####3.波形拉伸#####
def time_stretch(x, rate):
# rate:拉伸的尺寸,
# rate > 1 加快速度
# rate < 1 放慢速度
return librosa.effects.time_stretch(x, rate)
#####4.音高修正#####
def pitch_shifting(x, sr, n_steps, bins_per_octave=12):
# sr: 音频采样率
# n_steps: 要移动多少步
# bins_per_octave: 每个八度音阶(半音)多少步
return librosa.effects.pitch_shift(x, sr, n_steps, bins_per_octave=bins_per_octave)
data_noise = add_noise(wav_data)
data_stretch = time_stretch(wav_data, rate=2)
data_pitch2 = pitch_shifting(wav_data, fs, n_steps=-6, bins_per_octave=12) # 向下移三音(如果bins_per_octave为12,则六步)
# 绘图
plt.subplot(2, 2, 1)
plt.title("波形图", fontsize=15)
time = np.arange(0, len(wav_data)) * (1.0 / fs)
plt.plot(time, wav_data)
plt.xlabel('秒/s', fontsize=15)
plt.ylabel('振幅', fontsize=15)
plt.subplot(2, 2, 2)
plt.title("加噪", fontsize=15)
plt.plot(time, data_noise)
plt.xlabel('秒/s', fontsize=15)
plt.ylabel('振幅/Hz', fontsize=15)
plt.subplot(2, 2, 4)
plt.title("高音修正", fontsize=15)
plt.plot(time, data_pitch2)
plt.xlabel('秒/s', fontsize=15)
plt.ylabel('振幅/Hz', fontsize=15)
plt.subplot(2, 2, 3)
plt.title("波形拉伸", fontsize=15)
time = np.arange(0, len(data_stretch)) * (1.0 / fs)
plt.plot(time, data_stretch)
plt.xlabel('秒/s', fontsize=15)
plt.ylabel('振幅/Hz', fontsize=15)
plt.tight_layout()
plt.show()
2.3 高速フーリエ変換 (FFT)
終点検出 (またはデータ拡張) 後に得られた信号に対して高速フーリエ変換を実行して、その振幅周波数特性を取得します。信号を表す周波数領域信号の最初の 1000 (またはそれ以上) データを抽出します。
フーリエ変換を物理的な観点から見る, これは実際に、時間領域で信号を分析する従来の方法を、周波数領域で問題を分析することを考えることに変えるのに役立ちます。次の 3 次元図は、この角度の変換をより深く理解するのに役立ちます
。フーリエ変換すると、異なる正弦波信号が重ね合わされたものになります。次に、これらの正弦波の周波数を解析して、信号を周波数領域に変換します。信号によっては、時間領域で特性を把握することが困難なものもあります。周波数領域に変換すると特性が分かりやすいため、多くの信号解析では FFT 変換が使用されます。また、FFT は信号のスペクトルを抽出することができるため、スペクトル解析でもよく使用されます。
コンピュータの場合、離散的で有限長のデータのみを処理できます。他の変換タイプは数学的計算でのみ使用できます。コンピュータの前では、DFT 手法のみを使用できます。FFT は DFT の高速バージョンにすぎません。アルゴリズム。
fftの実装方法については、numpyにfftライブラリがあり、
具体的なプログラム例は以下の通りです。
import numpy.fft as nf
import numpy as np
from sklearn import preprocessing
import matplotlib.pyplot as plt
import os
import re
from scipy.io import wavfile
# coding=utf-8
import os
import shutil
import pandas as pd
import numpy as np
import scipy.io as sio
# 对原始数据进行fft快速傅里叶变换之后,
# 每个类别整体对数据进行归一化并写入csv文件里存储数据
def myfft(sourceDir, targetDir):
# 列出源目录文件和文件夹
for file in os.listdir(sourceDir):
# 拼接完整路径
sourceFile = os.path.join(sourceDir, file)
for files in os.listdir(sourceFile):
sourceFile1 = os.path.join(sourceFile, files)
data = []
for files1 in os.listdir(sourceFile1):
# 对每一个音频数据进行fft快速傅里叶变换
try:
rate, data1 = wavfile.read(f'{
sourceDir}/{
file}/{
files}/{
files1}')
xf = np.fft.fft(data1) # 快速傅里叶变换
xff = np.abs(xf) # 取复数的绝对值,即复数的模(双边频谱)
n = 2 ** 15 # n>2*rate 即>32000 ,取了2^15 = 32768
y = xff / n # 归一化处理
y1 = y[0:(int(n / 2))] # 由于对称性,只取一半区间
data.append(y1[0:1000]) #取前一千个数据点
except Exception as e:
continue
data = np.array(data)
print(data.shape)
#对所有的数据进行标准化处理
zscore_scaler = preprocessing.StandardScaler()
data2 = zscore_scaler.fit_transform(data)
test = pd.DataFrame(data=data2)
test.to_csv(f'{
targetDir}/{
file}{
files}.csv', encoding='utf-8', header=False,index=False) # 保存为csv格式文件
if __name__ == "__main__":
# 每个类别分别进行一次,完成之后得到4个.csv文件
myfft("F:/文件/文件/watermelon/watermelon_data/split_data",
"F:/文件/文件/watermelon/make_DataSet1")
単一オーディオ FFT レンダリング
2.4 データセットの作成
合計 900 以上のオリジナル オーディオ トラックがあり、エンドポイント検出データの強化後は 60,000 以上に拡張されました。
スイカの熟度とそれに対応するラベル
成熟 | ラベル |
---|---|
ミディアムレア | 0 |
成熟した | 1 |
熟れすぎた | 2 |
部分的に生まれた | 3 |
データセットのパーティショニング
データセットを分割する理由
ディープラーニングでは、多数の線形分類器または非線形分類器、微分可能または微分不可能な励起関数、およびプーリング層を使用して、観測オブジェクトの特性を自動的に抽出します。
このような優れた分類能力があると、2 つの問題が発生します:
複雑なネットワークでは、非常に多くの W が長い間統計における重みの意味を失っており、明確な物理的説明が得られず、リバース エンジニアリングを効果的に実行できません
。サンプルに含まれるノイズ情報や特殊なケースの情報を含む、さまざまな情報。
理由:
サンプルが少なすぎると、共通の特徴を一般化できない パラメータが多すぎると、非常に複雑な特徴内容を当てはめることができる
改善の理由:サンプル数を増やすと、より多くの特徴が得られるチェック方法
: 検証のためにいくつかのサンプルを採取します 通常、取得されたすべてのサンプル データは 3 つのセットに分割されます。
トレーニング セット: 学習に使用されるサンプル セット。これらのベクトルは、ネットワーク内の特定の係数をそれぞれ決定するために使用されます。モデルのトレーニングに使用されます。
検証セット: 分類器のパラメーターを調整するために使用されるサンプル セットです。トレーニング プロセス中、ネットワーク モデルは検証セット上ですぐに検証されます。同時に、モデルが検証セット データに対してどのように実行されるか、また、モデルが検証セット データに対してどのように実行されるかを観察します。損失関数の値は、精度は低下しますか? 精度は向上しますか? ハイパーパラメータを調整するために使用されます。
テストセット: トレーニング後のモデルの能力、主に分類能力をテストするために設定されたデータセットの一部であり、モデルの精度をテストし、汎化能力を評価するために使用されます。
データは
小規模なデータセットであるため、データ分割比率は7:3:1比率はトレーニング セット、テスト セット、検証セットに分かれており、トレーニング セットとテスト セットを 70% と 30% の割合に分け、トレーニング セットから 10% を検証セットとして抽出します。
処理概要
:オリジナルオーディオの終点検出とデータ強調後、高速フーリエ変換を実行し、各オーディオ信号の最初の1000データポイントを特性値として、
各成熟度レベルのオーディオデータ特性値をまとめます. 、後続のモデルトレーニングのために csv 形式で保存されます。
4つのカテゴリのCSVデータを読み込み、それに応じてラベルを付ける
プログラムコードは次のとおりです。
# 汇总所有的数据,并绘制标签
def makeall(file0, file1, file2, file3, name, savefile):
eight_medium = pd.read_csv(file0, header=None) # 8分熟
mature = pd.read_csv(file1, header=None) # 成熟
overrripe = pd.read_csv(file2, header=None) # 过熟
ripe_yet = pd.read_csv(file3, header=None) # 偏生
bfs_data = np.asarray(eight_medium)
cs_data = np.asarray(mature)
gs_data = np.asarray(overrripe)
ps_data = np.asarray(ripe_yet)
data = np.concatenate((bfs_data, cs_data, gs_data, ps_data))
bfs_label = np.zeros((bfs_data.shape[0], 1))
cs_label = np.ones((cs_data.shape[0], 1))
gs_label = 2 * np.ones((gs_data.shape[0], 1))
ps_label = 3 * np.ones((ps_data.shape[0], 1))
label = np.concatenate(((bfs_label, cs_label, gs_label, ps_label)))
variable = pd.DataFrame(label) # 将变量转化为dataframe数据结构
variable.to_csv(f'{
savefile}/{
name}_label.csv', header=None, index=None)
variable = pd.DataFrame(data) # 将变量转化为dataframe数据结构
variable.to_csv(f'{
savefile}/{
name}_data.csv', header=None, index=None) # 存储为没有表头和索引的csv文件
if __name__ == "__main__":
savefile = './makelabel/z-score_fft_alldata_0'
name = 'train'
savename = 'train_all'
t0_train = f'{
savefile}/{
name}0.csv'
t1_train = f'{
savefile}/{
name}1.csv'
t2_train = f'{
savefile}/{
name}2.csv'
t3_train = f'{
savefile}/{
name}3.csv'
makeall(t0_train, t1_train, t2_train, t3_train, savename, savefile)
2. モデルのトレーニング
1.モデル設計
提案されたモデル設計は、単純な畳み込みニューラル ネットワークに基づいており、その構造に追加と改良が加えられています。
Tensorflow
ディープ ラーニング Tensorflow フレームワーク: TensorFlow を使用すると、サーバー、エッジ デバイス、ネットワークのいずれにおいても、モデルのトレーニングとデプロイが容易になり、速度やパフォーマンスを犠牲にすることなく高度なモデルの構築とトレーニングが可能になります。
Keras
tf.keras は、TensorFlow 2.0 の高レベル API インターフェイスです。TensorFlow コードの新しいスタイルと設計パターンを提供し、TF コードの単純さと再利用性を大幅に向上させます。当局は、モデルの設計と開発に tf.keras を使用することも推奨しています。 Keras
の深層学習モデルであるユニバーサル モデル (モデル機能モデル) を使用してモデルを定義します。ユニバーサル モデルは、任意のトポロジで非常に複雑なニューラル ネットワークを設計できます。シーケンシャル モデル (Sequential) と比較して、線形にしか設計できません。層を追加することにより、一般モデルはネットワーク構造をより柔軟に構築し、各レベル間の関係を設定できます。
機能モデルインターフェイスは、ユーザーがマルチ出力モデル、非巡回有向モデル、またはモデルなどの複雑なモデルを定義する方法です。言い換えれば、モデルが VGG のようなワンストップ モデルでない限り、またはモデルが複数の出力を必要とする限り、常に機能モデルを選択する必要があります。機能モデルは最も一般的なタイプのモデルです。 、シーケンシャル モデル (シーケンシャル) はその特殊なケースにすぎません。
CNN
畳み込みニューラル ネットワーク (CNN) は、画像や音声などのグリッド状構造のデータを処理するためによく使用されるフィードフォワード ニューラル ネットワークです。CNN は、手動介入なしで特徴を自動的に抽出できるため、画像と音声の認識に優れています。
CNN の中核は畳み込み層であり、小さなウィンドウ (畳み込みカーネルと呼ばれる) をスライドさせることで入力データに対して畳み込み演算を実行し、局所的な特徴を抽出します。通常、畳み込み層の後にはプーリング層 (プーリング層) が続きます。これは、特徴マップの次元を削減し、計算量を削減するために使用されます。
畳み込みネットワークには 2 つの大きな特徴があります:
① 特徴を抽出するために少なくとも 1 つの畳み込み層が使用される。
② 畳み込みネットワークの畳み込み層は重み共有を通じて機能するため、重み W の数が大幅に削減され、トレーニング中に次のことが可能になります。同じ認識率に達した場合の収束速度は、完全に接続された BP ネットワークの収束速度よりも大幅に高速になります。
以下は、単純な畳み込みニューラル ネットワークに基づく 1 次元畳み込みニューラル ネットワーク (1DCNN) です。
モデル設計は次のとおりです。
モデル設計の各層に固有のパラメータ
レイヤー(種類) | カーネルサイズ | フィルター番号 | 出力形状 |
---|---|---|---|
conv1d | 3×1 | 8 | ( 1000, 8) |
max_pooling1d | ( 1000, 8) | ||
バッチ正規化 | ( 1000, 8) | ||
conv1d_1 | 3×1 | 16 | ( 1000, 16) |
max_pooling1d_1 | ( 1000, 16) | ||
バッチ_正規化_1 | ( 1000, 16) | ||
conv1d_2 | 3×1 | 32 | ( 1000, 32) |
max_pooling1d_2 | ( 500, 32) | ||
バッチ_正規化_2 | ( 500, 32) | ||
平らにする | 16000 | ||
ドロップアウト | 16000 | ||
密集 | 32 | 32 | |
密_2 | 4 | 4 |
手順は次のとおりです
def mymodel():
inputs = keras.Input(shape=(1000, 0))
h1 = layers.Conv1D(filters=8, kernel_size=3, strides=1, padding='same', activation='relu')(inputs)
h1 = layers.MaxPool1D(pool_size=2, strides=1, padding='same')(h1)
h1 = layers.BatchNormalization()(h1)
h2 = layers.Conv1D(filters=16, kernel_size=3, strides=1, padding='same', activation='relu' )(h1)
h2 = layers.MaxPool1D(pool_size=2, strides=1, padding='same')(h2)
h2 = layers.BatchNormalization()(h2)
h2 = layers.Conv1D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(h2)
h2 = layers.MaxPool1D(pool_size=2, strides=2, padding='same')(h2)
h2 = layers.BatchNormalization()(h2)
h3 = layers.Flatten()(h2) # 扁平层,方便全连接层传入数据
h4 = layers.Dropout(0.2)(h3) # Droupt层舍弃百分之20的神经元
h4 = layers.GaussianNoise(0.005)(h4)
h4 = layers.Dense(32, activation='relu')(h4) # 全连接层,输出为32
outputs = layers.Dense(4, activation='softmax')(h4) # 再来个全连接层,分类结果为4种
deep_model = keras.Model(inputs, outputs, name='1DCNN') # 整合每个层,搭建1DCNN模型成功
return deep_model
2. モデルトレーニングのハイパーパラメータ設定
ハイパーパラメータは、通常、機械学習アルゴリズムのトレーニング ステップの開始前に設定されたいくつかのパラメータ値を指します。これらのパラメータ値は、通常、アルゴリズム自体では学習できませんが、対照的に、アルゴリズム内で学習できます。または、重み w やバイアス b などの学習されたパラメータ。
バッチ トレーニング パラメーター
ニューラル ネットワークをトレーニングするときは、バッチとエポックという 2 つのパラメーターを設定する必要があります。1
つ目は (バッチ) サイズ、バッチ内のサンプルの総数、つまり、トレーニングに使用されるサンプルの数です。ネットワークをトレーニングするとき、ネットワークに入力されるすべてのデータには多大な計算が必要です。通常、データをいくつかのバッチに分割し、バッチごとにネットワークに渡し、各バッチが送信された後にパラメータを更新します。 2 つの利点があり、一方では、勾配内のすべてのデータが共同してこの勾配降下の方向を決定するバッチであるため、下降中に道を誤ることは容易ではなく、ランダム性が低下します。バッチ内のサンプルはデータ セット全体よりもはるかに小さいため、計算量も少なくなります。それほど大きくはありません。
毎回読み取られるトレーニング セットの数はバッチ サイズと呼ばれます。畳み込みニューラル ネットワークでは、通常、大きなバッチは、ネットワークの収束を高速化します。ただし、メモリ リソースの制限により、バッチが大きすぎます。メモリ不足やプログラム カーネルのクラッシュが発生する可能性があります。Bath_size の値は通常、[16, 32, 64, 128] です。
次に、 1 回のトレーニングですべてのサンプル データをニューラル ネットワーク モデルに入れることを 1 エポックと呼びます。
すべてのサンプルの数が 1,000 であると仮定すると、バッチ サイズを 10 に設定します。つまり、トレーニングのために一度に 10 個のデータを読み取ります。トレーニングを完了するには、データのトレーニング ラウンドを 100 回読み取る必要があります。
この設計では、エポックは 100 に設定され、バッチ サイズは 128 に設定されています。
損失関数
損失関数は、モデルの予測結果と実際の結果との差を測定するために使用される関数です。機械学習では、通常、モデルがデータによりよく適合するようにモデルのパラメーターを最適化するために損失関数を使用します。一般的な損失関数には、平均二乗誤差、クロスエントロピーなどが含まれます。この設計では、「sparse_categorical_accuracy」を選択しました。
学習率
学習率 (学習率または lr) は、最適化アルゴリズムにおけるネットワークの重みの更新の大きさを指します。学習率は、パフォーマンスに最も影響を与えるハイパーパラメーターの 1 つです。他のハイパーパラメーターと比較して、学習率の調整は、より効果的な方法です。モデルの有効容量を制御します。したがって、ニューラル ネットワークをトレーニングするために、設定する必要がある重要なハイパーパラメータの 1 つは学習率です。最適な学習率を選択することが重要です。学習率は
一定でも、または一定でも構いません。徐々に減少、モメンタムベースまたは適応型。最適化アルゴリズムが異なると、学習率も異なります。学習率が大きすぎると、モデルは収束しない可能性があり、損失は上下に振動し続けます。学習率が小さすぎると、モデルは収束しません。収束が遅いため、より長い学習時間が必要になります。通常、lr の値は [0.01, 0.001, 0.0001] ですが、
この設計の学習率は 0.001 に設定されています。
オプティマイザー
データ、モデル、損失関数が決定されると、タスクの数学的モデルが決定され、モデルを最適化するために適切なオプティマイザー (Optimizer) を選択する必要があります。オプティマイザーの基本
クラスは、勾配損失を計算するためのメソッドを提供します。変数に勾配と勾配を適用できます。最も一般的に使用されるオプティマイザーは SGDM と Adam. (SGD) 収束は遅いですが、勢いを追加すると収束を高速化できます。同時に、勢いを伴う確率的勾配降下アルゴリズムには、 SGDM は CV で広く使用されていますが、Adam は基本的に NLP、RL、GAN、音声合成などの分野を網羅しています。 NLP の分野では、Transformer や BERT などの古典的なモデルはすべて Adam とその亜種 AdamW を使用する
ため、今回学習率の適応学習のために設計されたアルゴリズムが Adam 学習率最適化アルゴリズムです。
3. モデル性能評価指標
ディープラーニングでは、モデルの品質を測定することが重要な課題です。モデルの評価とは、ニューラルネットワークで当てはめたモデルが優れているかどうかを判断することです。多くの場合、モデルの品質を一目で判断することは難しいため、モデル評価指標が登場して混乱しています. マトリックスはモデルの結果を判断するための指標の 1 つです. 混同マトリックスは、分類モデルの精度を計算するための最も基本的で、最も直感的で、最も簡単な方法です。混同行列は、分類モデルによって間違ったクラスと正しいクラスにそれぞれ分類された観測値の数をカウントし、その結果を表に表示します。2 分類モデルを例に取ると、混同行列の形式は次のようになります。形。
混同行列プロット
4. モデルのトレーニング
モデルの設計とモデル トレーニングのハイパーパラメーター設定を決定した後、モデルのトレーニングが開始されます。
実装コードは以下の通り
"""
本段本代码是进行模型训练
"""
import os
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "1"
#os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from tensorflow.keras import layers, models, Model, Sequential
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, Sequential
from numpy import random
#固定随机种子,在调用seed_tensorflow后还需设置model.fit中shuffle=False、worker=1.
#保证每次训练结果一致
def seed_tensorflow(seed=42):
os.environ['PYTHONHASHSEED'] = str(seed)
random.seed(seed)
np.random.seed(seed)
tf.random.set_seed(seed)
os.environ['TF_DETERMINISTIC_OPS'] = '1'
seed_tensorflow(42)
def read_csv_file(train_data_file_path, train_label_file_path):
"""
读取csv文件并将文件进行拼接
:param train_data_file_path: 训练数据路径
:param train_label_file_path: 训练标签路径
:return: 返回拼接完成后的路径
"""
# 从csv中读取数据
train_data = pd.read_csv(train_data_file_path, header=None)
train_label = pd.read_csv(train_label_file_path, header=None)
# 将数据集拼接起来
# 数据与标签拼接
dataset_train = pd.concat([train_data, train_label], axis=1)
dataset = pd.concat([dataset_train], axis=0).sample(frac=1, random_state=0).reset_index(drop=True)
return dataset
def get_train_test(dataset, data_ndim=1):
# 获得训练数据和标签
X_train = dataset.iloc[:, :-1]
y_train = dataset.iloc[:, -1]
# 为了增加模型的泛化能力,需要打乱数据集
index = [i for i in range(len(X_train))]
random.seed(42)
random.shuffle(index)
X_train = np.array(X_train)[index]
y_train = np.array(y_train)[index]
# 改变数据维度让他符合(数量,长度,维度)的要求
X_train = np.array(X_train).reshape(X_train.shape[0], X_train.shape[1], data_ndim)
print("X shape: ", X_train.shape)
return X_train, y_train
# 保存最佳模型
class CustomModelCheckpoint(keras.callbacks.Callback): # 使用回调函数来观察训练过程中网络内部的状态和统计信息r然后选取最佳的进行保存
def __init__(self, model, path): # (自定义初始化)
self.model = model
self.path = path
self.best_loss = np.inf # np.inf 表示+∞,是没有确切的数值的,类型为浮点型 自定义最佳损失数值
def on_epoch_end(self, epoch, logs=None): # on_epoch_end(self, epoch, logs=None)在每次迭代训练结束时调用。在不同的方法中这个logs有不同的键值
val_loss = logs['val_loss'] # logs是一个字典对象directory;
if val_loss < self.best_loss:
print("\nValidation loss decreased from {} to {}, saving model".format(self.best_loss, val_loss))
self.model.save_weights(self.path, overwrite=True) # overwrite=True覆盖原有文件 # 此处为保存权重没有保存整个模型
self.best_loss = val_loss
def mymodel():
inputs = keras.Input(shape=(1000, 1))
h1 = layers.Conv1D(filters=8, kernel_size=3, strides=1, padding='same', activation='relu')(inputs)
h1 = layers.MaxPool1D(pool_size=2, strides=1, padding='same')(h1)
h1 = layers.BatchNormalization()(h1)
h2 = layers.Conv1D(filters=16, kernel_size=3, strides=1, padding='same', activation='relu')(h1)
h2 = layers.MaxPool1D(pool_size=2, strides=1, padding='same')(h2)
h2 = layers.BatchNormalization()(h2)
h2 = layers.Conv1D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(h2)
h2 = layers.MaxPool1D(pool_size=2, strides=2, padding='same')(h2)
h2 = layers.BatchNormalization()(h2)
h3 = layers.Flatten()(h2) # 扁平层,方便全连接层传入数据
h4 = layers.Dropout(0.2)(h3) # Droupt层舍弃百分之20的神经元
h4 = layers.GaussianNoise(0.005)(h4)
h4 = layers.Dense(32, activation='relu' )(h4) # 全连接层,输出为32
outputs = layers.Dense(4, activation='softmax')(h4) # 再来个全连接层,分类结果为4种
deep_model = keras.Model(inputs, outputs, name='1DCNN') # 整合每个层,搭建1DCNN模型成功
return deep_model
def bulid(X_train, y_train, X_test, y_test,X_val,y_val, batch_size=128, epochs=100):
"""
搭建网络结构完成训练
:param X_train: 训练集数据
:param y_train: 训练集标签
:param X_test: 测试集数据
:param y_test: 测试集标签
:param X_val: 验证集数据
:param y_val: 验证集标签
:param batch_size: 批次大小
:param epochs: 循环轮数
:return: acc和loss曲线
"""
model = mymodel()
model.compile(optimizer=tf.keras.optimizers.Adam(lr = 0.001,decay=1e-3),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
history = model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size,
validation_data=(X_val,y_val),
workers =1,
callbacks=[CustomModelCheckpoint(model, r'mybestcnn_minmax_fft_4.h5')])
keras.models.save_model(model,'./mycnn.h5')
model.summary()
# 获得训练集和测试集的acc和loss曲线
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
# 评估模型
scores = model.evaluate(X_test, y_test, verbose=1)
print('%s: %.2f%%' % (model.metrics_names[1], scores[1] * 100))
y_predict = model.predict(X_test)
y_pred_int = np.argmax(y_predict, axis=1)
print(classification_report(y_test, y_pred_int, digits=4))
# 绘制acc曲线
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
# 绘制loss曲线
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
# 绘制混淆矩阵
y_pred_gailv = model.predict(X_test, verbose=1)
y_pred_int = np.argmax(y_pred_gailv, axis=1)
con_mat = confusion_matrix(y_test.astype(str), y_pred_int.astype(str))
con_mat = np.delete(con_mat, [0, 2, 4, 6], axis=0)
con_mat = np.delete(con_mat, [1, 3, 5, 7], axis=1)
classes = list(set(y_train))
classes.sort()
# plt.imshow(con_mat, cmap=plt.cm.Blues)
plt.imshow(con_mat, cmap='Blues')
indices = range(len(con_mat))
plt.xticks(indices, classes)
plt.yticks(indices, classes)
plt.colorbar()
plt.title('Confusion Matrix')
plt.xlabel('guess')
plt.ylabel('true')
for first_index in range(len(con_mat)):
for second_index in range(len(con_mat[first_index])):
plt.text(first_index, second_index, con_mat[second_index][first_index], va='center', ha='center')
plt.show()
if __name__ == "__main__":
"""
频域数据集
"""
#训练集
x_train_csv_path = f'./makelabel/train_all_data.csv'
y_train_csv_path = f'./makelabel/train_all_label.csv'
dataset1 = read_csv_file(x_train_csv_path, y_train_csv_path)
X_train, y_train = get_train_test(dataset=dataset1, data_ndim=1)
#测试集
x_test_csv_path = f'./makelabel/test_all_data.csv'
y_test_csv_path = f'./makelabel/test_all_label.csv'
dataset2 = read_csv_file(x_test_csv_path, y_test_csv_path)
X_test, y_test = get_train_test(dataset=dataset2, data_ndim=1)
#验证集
x_val_csv_path = f'./makelabel/val_all_data.csv'
y_val_csv_path = f'./makelabel/val_all_label.csv'
dataset3 = read_csv_file(x_val_csv_path, y_val_csv_path)
X_val, y_val = get_train_test(dataset=dataset3, data_ndim=1)
# 模型训练
bulid(X_train, y_train, X_test, y_test,X_val,y_val)
5. モデルのトレーニング結果と分析
5.1 3種類のデータセットの学習結果
図からわかるように、3 つのデータ検証セットの精度は 99% に達する可能性があり、
その中で Q データ セットが最も高い精度を持っており、
20 エポックのトレーニング後に 3 つのデータ セットはすべて 98% 以上に達しました。
各データセットのテストセットをテストした後に得られた混同行列を通じて、表に示すように、各成熟度レベルでのモデルの検出効果が分析され、3 つの
データセットの個別認識率は 99% に達し、平均精度も 99% に達しており、
上記のデータは、分類モデルが非常に堅牢であり、スイカの成熟度をより適切に検出できることを示しています。
F1 スコア
データセット
|
ミディアムレア | 成熟した | 熟れすぎた | 部分的に生まれた | 平均 |
---|---|---|---|---|---|
Q | 0.9958 | 0.9972 | 0.9975 | 0.9954 | 0.9965 |
P | 0.9950 | 0.9942 | 0.9948 | 0.9926 | 0.9941 |
T | 0.9924 | 0.9955 | 0.9945 | 0.9925 | 0.9937 |
全て | 0.9935 | 0.9983 | 0.9973 | 0.9948 | 0.9960 |
異なるタッピング方法が混合された場合のモデルの認識効果をテストするために、すべてのデータが融合されてトレーニングされます。トレーニング曲線は図に示すとおりです
。100
エポック後、モデルの検証セットの精度は 99.66% に達し、損失は 0.02 に収束します。
下の図は、すべてのデータでテスト セットをテストした後に得られた混同行列を示しています。
混同行列は、モデルにまだいくつかの誤った判断があることを示しています。
混同行列を通じて、テスト セット上のモデルの平均精度は 99.78% として取得できます。 。
5.2 モデルのパフォーマンスの比較
モデルのパフォーマンスを比較するために, 比較実験のためにいくつかの古典的な畳み込みニューラル ネットワーク モデルが選択されます.
選択された畳み込みニューラル ネットワーク モデルはすべて 2 次元および 1 次元に適応されています.
この記事で選択された畳み込みモデルは ResNet18, MobileNet です、AlexNet、VGGNet、および GoogLeNet を比較実験に使用しました。実験前に、同じトレーニング セット、学習率、バッチ トレーニング パラメーター (エポック、バッチ サイズ)、オプティマイザー、および損失関数を使用して同じトレーニング プロセスを実行しました。
各モデルの認識結果を表に示します。
モデルの識別 | モデルパラメータ量 | 単一カテゴリ | 平均 | |||
---|---|---|---|---|---|---|
ミディアムレア | 成熟した | 熟れすぎた | 部分的に生まれた | |||
1DCNN | 514,516 | 0.9935 | 0.9983 | 0.9973 | 0.9948 | 0.9960 |
VGG16 | 41,619,780 | 0.9855 | 0.9967 | 0.9972 | 0.9855 | 0.9912 |
アレックスネット | 12,360,900 | 0.9757 | 0.9928 | 0.9913 | 0.9797 | 0.9849 |
グーゲルネット | 3,527,476 | 0.9792 | 0.9945 | 0.9938 | 0.9803 | 0.9870 |
レスネット18 | 3,856,772 | 0.9403 | 0.9741 | 0.9730 | 0.9587 | 0.9615 |
モバイルネット | 487,711 | 0.8765 | 0.9610 | 0.9607 | 0.9344 | 0.9332 |
最後に、
記事に間違いがございましたら、コメント欄またはプライベートメッセージでお気軽にご指摘ください。
初心者や初心者の方はまだまだ勉強中ですので、ハイハンよろしくお願いします~