機械学習 - 線形モデル (ボストンの住宅価格予測)

機械学習 - 線形モデル (ボストンの住宅価格予測)

人工知能、機械学習、深層学習の関係

ここに画像の説明を挿入

  • 人工知能 (AI) は、人間の知性をシミュレートし、拡張し、拡大するための理論、方法、技術、およびアプリケーション システムを開発する技術科学です. この定義は、目標を設定するだけで、方法を制限するものではありません.
  • 機械学習 (Machine Learning、ML) は現在、人工知能を実現するためのより効果的な方法です。
  • ディープ ラーニング (DL) は、機械学習アルゴリズムの最も一般的なブランチであり、ほとんどの従来の機械学習アルゴリズムに取って代わります。

機械学習

機械学習は、コンピューターを使用して人間の学習行動をシミュレートまたは実現し、新しい知識やスキルを習得し、既存の知識構造を再編成して自分のパフォーマンスを継続的に改善する特別な研究です。

機械学習の実装は、トレーニングと予測の 2 つのステップに分けることができます。これは、帰納法と演繹法に似ています。

  • 帰納法: 特定の例から一般法則を抽象化すること。つまり、一定数のサンプル (既知の入力 x と出力 y) から、出力 y と入力 x の関係を学習します。
  • 演繹: 特定の場合について、一般的な法律から結果を導き出すこと。トレーニングから得られた y と x の関係に基づいて、新しい x から y が計算されます。

モデルによって計算された出力が実際のシーンの出力と一致する場合、モデルは有効です。モデルが有効であるための基本的な条件は、既知のサンプルに適合できることです。

損失関数 (Loss): モデルの予測値と実際の値のギャップを測定する評価関数

ここに画像の説明を挿入

モデルの 3 つの重要な要素 (モデルの仮定評価関数最適化アルゴリズム)を決定します。

損失を最小化することがモデルの最適化の目標であり、損失を最小化する方法は最適化アルゴリズムと呼ばれます。

機械学習タスクのフレームワーク、その学習の本質はパラメーター推定であり、未知の目的関数fffとトレーニング サンプルDD仮説セットHHからの基底としてのDH、学習アルゴリズムAAA は関数gggggg はトレーニング サンプルDD を最大限にD、関数gggは目的関数fff
ここに画像の説明を挿入

ディープラーニング

現在、ほとんどの機械学習タスクは、特に音声、コンピューター ビジョン、および自然言語処理の分野で、ディープ ラーニング モデルを使用して解決できます. ディープ ラーニング モデルの効果は、従来の機械学習アルゴリズムと比較して大幅に改善されています.

機械学習も深層学習も、モデルの仮定、評価関数、最適化アルゴリズムなどの理論構造は一致していますが、根本的な違いは仮定の複雑さにあります。

人工ニューラル ネットワークには、畳み込み層、全結合層、LSTM などの複数のニューラル ネットワーク層が含まれており、各層には多数のニューロンが含まれており、3 層以上の非線形ニューラル ネットワークをディープ ニューラル ネットワークと呼ぶことができます

深層学習モデルは、入力から出力へのマッピング関数と見なすことができ、十分に深いニューラル ネットワークは、理論的には複雑な関数に適合できます。ニューラル ネットワークは、サンプル データの内部法則と表現レベルを学習するのに非常に適しており、テキスト、画像、および音声タスクに適切に適用できます。

ディープラーニングは、人工知能を実現するための基礎として知られています

ここに画像の説明を挿入

  • ニューロン

    ニューラル ネットワークの各ノードはニューロンと呼ばれ、加重和と活性化関数の 2 つの部分で構成されます。

    • 加重合計: すべての出力の加重合計
    • 非線形変換 (活性化関数): 加重合計の結果は非線形関数によって変換され、ニューロン計算に非線形機能を持たせることができます。
  • 多層接続

    多数のニューロンが異なる層に配置され、相互に接続された多層構造を形成します。これをニューラル ネットワークと呼びます。

  • 前方計算

    ネットワークの前から順に、入力から出力を計算するプロセス。

  • 計算グラフ

    ニューラル ネットワークの計算ロジックをグラフィカルに表示します。

    ニューラルネットワークの計算グラフを数式で表現することも可能です。
    Y = f 3 ( f 2 ( f 1 ( w 1 · x 1 + w 2 · x 2 + . . + b) . . ) . . . ) Y = f3(f2(f1(w_1 x_1 + w_2 · x_2+ ... + b)...)...)=f 3 ( f 2 ( f 1 ( w1バツ1+w2バツ2+...+b ) ... ) ... )

ニューラル ネットワークは、本質的に多くのパラメーターを持つ大きな数式です。

ボストンの住宅価格予測

Python 言語と Numpy ライブラリを使用してニューラル ネットワーク モデルを構築する

データセットの紹介

ボストン地域の住宅価格は多くの要因の影響を受けます.データ セットは住宅価格とこのタイプの住宅の平均価格に影響を与える可能性のある 13 の要因を数えます.13 の要因に基づいて住宅価格を予測するためのモデルを構築することが期待されます.

ここに画像の説明を挿入

予測問題は、予測出力のタイプが連続実数値か離散ラベルかによって、回帰タスク分類タスクに分けることができます

モデルの仮定→ \rightarrow線形回帰モデル

住宅価格とさまざまな影響因子との間の線形関係は、y = ∑ j = 1 M xjwj + by=\sum_{j=1}^{M}x_jw_j+b で記述できると仮定します。
y=j = 1Mバツw+b
モデルの解決プロセスは、既知のサンプル データを通じて各wj wjw jbbb ,wj wjw jbbb は、それぞれ線形モデルの重みとバイアスを表します。

一次元の場合wj wjw jbbbは直線の傾きと切片です

評価関数→ \rightarrow平均二乗誤差

線形回帰モデルは、平均二乗誤差 (平均二乗誤差、MSE) を損失関数 (損失) として使用して、予測された住宅価格と実際の住宅価格の差を測定します。
MSE = 1 N ∑ i = 1 N ( Y i ^ − Y i ) 2 MSE = \frac{1}{N}\sum_{i=1}^{N}(\hat{Y_i} - Y_i)^2MSE=N1私は= 1N(^)2
各トレーニング サンプルのモデルの予測誤差が合計され、累積されて、サンプル全体の精度が測定されます。

損失関数の設計は、合理性(物理的な意味)だけでなく、解けやすさ(解りやすさ)も考慮すべき

線形回帰モデルのネットワーク構造

ニューラル ネットワークの標準的な構造では、各ニューロンは加重和と非線形変換で構成され、その後、複数のニューロンが配置され、層状に接続されてニューラル ネットワークが形成されます。

線形回帰モデルは、加重和のみを持ち、非線形変換を持たないニューロンであり、ネットワークを形成する必要はありません。

ここに画像の説明を挿入

ボストン住宅価格予測タスクの実装

さまざまなシナリオのディープ ラーニング モデルにはある程度の汎用性があり、基本的に 5 つのステップがあります。

  • 情報処理

    データを読み取り、前処理操作 (データの検証、フォーマットなど) を完了して、モデルを読み取れるようにする

  • モデル設計

    モデルの仮説空間に相当するネットワーク構造設計

  • トレーニング構成

    モデルが使用する解探索アルゴリズムとオプティマイザを設定し、コンピューティング リソースを指定します

  • トレーニングプロセス

    トレーニング プロセスはループで呼び出され、各ラウンドには、順方向計算損失関数 (最適化ターゲット) 逆伝播の3 つのステップが含まれます。

  • モデル保存

    トレーニング済みのモデルを保存し、モデルが予測するときにそれを呼び出します

異なるモデルを構築する場合、モデルの 3 つの要素 (モデルの仮定、評価関数、最適化アルゴリズム) のみが異なり、他の手順は基本的に同じです。

1. データ処理

データ処理には、データのインポートデータの次元変換データセットの分割データの正規化処理load_data 関数のカプセル化の 5 つの基本的な部分があります。

データが前処理された後、モデルによって呼び出すことができます。

データのインポート

import numpy as np
import json
# 读取训练数据
datafile = "./work/housing.data"
data = np.fromfile(datafile,sep=" ")

次元変換

読み取られた元のデータは 1 次元であり、すべてのデータが結合されており、次元を変換して 13 xx を含む行ごとに 1 つのデータ サンプル (14 の値) を持つ 2 次元マトリックスを形成する必要がありますx (住宅価格に影響する特徴) とyyy (住宅タイプの平均価格)

feature_names = [
    "CRIM",  "ZN",  "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD",
    "TAX", "PTRATIO", "B", "LSTAT", "MEDV"
]

feature_num = len(feature_names)
data = data.reshape([data.shape[0] // feature_num,feature_num])

データセット パーティション

データセットはトレーニングセットとテストセットに分けられ、トレーニングセットはモデルのパラメーターを決定するために使用され、テストセットはモデルの効果を評価するために使用されます

# 将80%的数据用作训练集,20%用作测试集
ratio = 0.8
offset = int(data.shape[0]*ratio)
training_data = data[:offset]

データの正規化

各特徴の値が 0 から 1 の間でスケーリングされるように各特徴を正規化すると、次の 2 つの利点があります。

  • モデルのトレーニングはより効率的です

  • 特徴の前の重みは、予測結果に対する変数の寄与を表すことができます

    各固有値自体は、正規化後に同じ範囲を持つため

maximums,minimums = training_data.max(axis=0),training_data.min(axis=0)
# 对数据进行归一化处理
for i in range(feature_num):
	data[:,i] = (data[:,i] - minimums[i]) / (maximums[i] - minimums[i])

入力機能の正規化は、将来的に統合学習ステップ サイズをより適切にすることも目的としています。

特徴入力が正規化された後、さまざまなパラメーターによる損失出力は比較的規則的な曲線になり、学習率は均一な値に設定できます。

特徴入力が正規化されていない場合, 異なる特徴に対応するパラメータに必要なステップ サイズは一定ではありません. スケールの大きなパラメータには大きなステップ サイズが必要であり, 小さなサイズのパラメータには小さなステップ サイズが必要です.学習率。

ここに画像の説明を挿入

正規化されていない特徴は、異なる特徴次元に対して異なる理想的なステップ サイズをもたらします

load_data 関数にカプセル化

上記のデータ処理操作をカプセル化して load_data 関数を形成し、次のステップでモデルを呼び出せるようにします

def load_data():
    # 从文件导入数据
    datafile = "./data/housing.data"
    data = np.fromfile(datafile,sep=" ")
    # 每条原始数据包含14项,其中前面13项是影响因素,第14项是相应的房屋价格平均数
    feature_names = [
    "CRIM",  "ZN",  "INDUS", "CHAS", "NOX", "RM", "AGE", "DIS", "RAD",
    "TAX", "PTRATIO", "B", "LSTAT", "MEDV"]

	feature_num = len(feature_names)
    # 将原始数据进行Reshape,变成[N,14]这样的形状
	data = data.reshape([data.shape[0] // feature_num,feature_num])
    # 将原始数据集拆分成训练集和测试集
    # 使用80%的数据做训练,20%的数据做测试
    # 测试集和训练集必须没有交集
    ratio = 0.8
    offset = int(data.shape[0] * ratio)
    # 计算训练集的最大值,最小值
    maximums,minimums = training_data.max(axis=0),training_data.min(axis=0)
    # 对数据进行归一化处理
    for i in range(feature_num):
        data[:,i] = (data[;,i] - minimums[i]) / (maximums[i] - minimums[i])
    # 训练集和测试集的划分
    training_data = data[:offset]
    test_data = data[offset:]
   	return training_data,test_data

2. モデル設計

モデル設計は、ディープ ラーニング モデルの重要な要素の 1 つであり、ネットワーク構造設計とも呼ばれ、モデルの前方計算プロセスを実現します。

入力機能xxxには 13 個のベクトルがあり、yyyには 1 つのベクトルがあり、パラメーターの重みの形状は13 × 1 13\times113×1

完全な線形回帰式では、オフセットbbも初期化する必要があります。b、線形回帰モデルの完全な出力は
z = t + bz=t+bです=t+b
機能とパラメータから出力値を計算するプロセスは、順計算と呼ばれます。

forward 関数を実装して、特徴とパラメーターから計算プロセスを完了し、予測値を出力します。

class Network():
    def __init__(self,num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights,1)
        self.b = 0
        
    def forward(self,x):
        # x -> (1,13) , w-> (13,1)
        z = np.dot(x,self.w) + self.b
        return z

3. トレーニング構成

モデルの設計が完了したら、トレーニング構成を通じてモデルの最適値を見つける、つまり、損失関数を通じてモデルの品質を測定する必要があります。

xx をモデル別に計算するxの影響要因に対応する住宅価格はzzz、実際のデータの住宅価格はyyy 、予測値zzを測定するための何らかの指標が必要ですzと真の値yyy間のギャップ

回帰問題の場合、最も一般的に使用される測定方法は、モデルの品質を評価する指標として平均二乗誤差を使用することです。
損失 = ( y − z ) 2 損失 = (yz)^2損失_=( _z )2
損失 損失損失​​通常損失関数と呼ばれ、モデルの品質を測定する指標です。

平均二乗誤差は回帰問題の損失関数としてよく使われ、交差エントロピー (Cross-Entropy) は分類問題の損失関数としてよく使われます。

損失関数の計算では各サンプルの損失関数値を考慮する必要があるため、1 つのサンプルの損失関数を合計し、それをサンプルの総数 NN で割る必要がありますN
損失 = 1 N ∑ i = 1 N ( yi − zi ) 2 損失 = \frac{1}{N}\sum_{i=1}^{N}(y_i-z_i)^2損失_=N1私は= 1N( _)2
Network クラスに損失関数を追加する

class Network():
    def __init__(self,num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights,1)
        self.b = 0
        
    def forward(self,x):
        # x -> (1,13) , w-> (13,1)
        z = np.dot(x,self.w) + self.b
        return z
    def loss(self,z,y):
        error = z-y
        loss = np.mean(error * error)
        return loss

4. トレーニング プロセス

以上がニューラルネットワークの構築過程であり、ニューラルネットワークを通じて予測値と損失関数の計算が完了する。

モデルの育成過程、完全なパラメータwwwbbbの解。

トレーニング プロセスの目標は、定義された損失関数をできるだけ小さくすること、つまりパラメトリックな解を見つけることですwwwbbb、損失関数が最小値を取得するようにします。

ここに画像の説明を挿入

特定の点での曲線の傾きは、その点での関数曲線の微分値に等しいためです。曲線の極点での勾配は 0 です。つまり、極点での関数の導関数は 0 です。損失関数がwwの最小値を取得するようにします。wbbbは、次の連立方程式の解でなければなりません。

∂ L ∂ w = 0 \frac{\partial{L}}{\partial{w}} = 0w∂L _=0

∂ L ∂ b = 0 \frac{\partial{L}}{\partial{b}} = 0∂b _∂L _=0

サンプルデータ( x , y ) (x,y)を取得します( x ,y )を上記の式に代入して、w と bw と bwbの値ですが、このアプローチは線形回帰のような単純なタスクにのみ有効です。モデルに非線形変換が含まれている場合、または損失関数が平均二乗誤差の単純な形式ではない場合、上記の式でそれを解くことは困難です。

より普遍的な数値解法:勾配降下法

1. 勾配降下 (GD)

暗号で広く使われている一方向性関数と呼ばれる、順方向に解くのは簡単だが、逆方向には解くのが難しい関数が多数あります。

コンビネーションロックの特徴は、キーが正しいかどうかをすばやく判断できることです(既知のxxx,求yyyは簡単ですが、パスワードロックシステムを取得しても解読できない正しい秘密鍵 (知られているyyyxx×は難しい)。

ニューラルネットワークモデルの損失関数は一方向関数であり、逆に解くのは容易ではありません。

勾配降下法、損失関数の最小値を解くという実現のアイデア: 現在のパラメーター値から、最低点に到達するまで下り坂の方向に段階的に進みます。

トレーニングの鍵は、( w , b ) (w,b)のセットを見つけることです( w b )、したがって、損失関数LLL は最小値を取ります。

損失関数カテゴリの選択:絶対値誤差平均二乗誤差の比較

  • 絶対値誤差の損失関数、微分不可
  • 平均二乗誤差の損失関数は微分できます

平均二乗誤差には 2 つの利点があります

  • 曲線の最低点は微分可能です
  • 最下点に近づくほど、曲線の傾きが徐々に遅くなり、現在の勾配がどの程度最下点に近づいているかを判断するのに役立ちます (最下点を見逃さないように、ステップ サイズを徐々に小さくするかどうかを検討できます)。

パラメータ更新プロセスで従うべき原則:

  • 損失が減少していることを保証する
  • できるだけ早く下降トレンド

勾配の反対方向は、関数の値が最も速く減少する方向です。

勾配を計算する

勾配の計算をより簡潔にするために (導出プロセスで係数 2 が生成されます)、係数1 2 \frac{1}{2}が導入されます。21,定义损失函数
L = 1 2 N ∑ i = 1 N ( y i − z i ) 2 L = \frac{1}{2N}\sum_{i=1}^{N}(y_i - z_i)^2 L=2N _1私は= 1N( _)2
其中 z i z_i はいはい_予測値zi = ∑ j = 0 12 xij ⋅ wj + b z_i = \sum_{j=0}^{12}x_i^j w_j + b
=j = 012バツw+b
段度の定义
gradient = ( ∂ L ∂ w 0 , ∂ L ∂ w 1 , . . , ∂ L ∂ w 12 , ∂ L ∂ b ) 勾配 = (\frac{\partial{L}}{\partial{ w_0}},\frac{\partial{L}}{\partial{w_1}},...,\frac{\partial{L}}{\partial{w_{12}}},\frac{\partial {L}}{\partial{b}})グラデーション_ _ _ _ _ _ _=(w0∂L _w1∂L _... ,w12∂L _∂b _∂L _) LL
を計算するえへへww_wbbbの偏导数
∂ L ∂ wj = 1 N ∑ i = 1 N ( zi − yi ) ∂ zi ∂ wj = 1 N ∑ i = 1 N ( zi − yi ) xij \frac{\partial{L}}{\ partial{w_j}} = \frac{1}{N}\sum_{i=1}^N(z_i-y_i)\frac{\partial{z_i}}{\partial{w_j}}= \frac{1} {N}\sum_{i=1}{N}(z_i-y_i)x_i^jw∂L _=N1私は= 1N( zy)w∂z _=N1私は= 1N ( zy) ×

∂ L ∂ b = 1 N ∑ i = 1 N ( zi − yi ) ∂ zi ∂ b = 1 N ∑ i = 1 N ( zi − yi ) \frac{\partial{L}}{\partial{b}} = \frac{1}{N}\sum_{i=1}^N(z_i - y_i)\frac{\partial{z_i}}{\partial{b}} = \frac{1}{N}\sum_ {i=1}^{N}(z_i-y_i)∂b _∂L _=N1私は= 1N( zy)∂b _∂z _=N1私は= 1N( zy)

Numpy のブロードキャスト メカニズムにより、計算プロセスがより簡潔になります。

別の観点から総勾配を考えてみましょう。

各サンプルには勾配への寄与があり、総勾配は勾配へのサンプルの寄与の平均値です
∂ L ∂ wj = 1 N ∑ i = 1 N ( zi − yi ) ∂ zi ∂ wj = 1 N ∑ i = 1 N ( zi − yi ) xij \frac{\partial{L}}{\partial{w_j}} = \frac{1}{N}\sum_{i=1}^N(z_i-y_i)\frac {\ partial{z_i}}{\partial{w_j}}= \frac{1}{N}\sum_{i=1}{N}(z_i-y_i)x_i^jw∂L _=N1私は= 1N( zy)w∂z _=N1私は= 1N ( zy) ×
Network クラスの勾配関数を増やす

class Network():
    def __init__(self,num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights,1)
        self.b = 0
        
    def forward(self,x):
        # x -> (1,13) , w-> (13,1)
        z = np.dot(x,self.w) + self.b
        return z
    def loss(self,z,y):
        error = z-y
        loss = np.mean(error * error)
        return loss
    def gradient(self,x,y):
        z = self.forward(x)
        gradient_w = (z-y)*x
        gradient_w = np.mean(gradient_w,axis=0)
        gradient_w = gradient_w[:,np.newaxis]
        
        gradient_b = (z-y)
        gradient_b = np.mean(gradient_b)
        return gradient_w,gradient_b

損失関数が小さくなる点を特定し、勾配法を更新して、勾配の反対方向に小さなステップを移動します。

net.w[5] = net.w[5] - eta *gradient_w5
  • 減算、パラメータは勾配の反対方向に移動する必要があります
  • eta: 勾配の反対方向に沿った各パラメーター値のサイズ、つまり、学習率と呼ばれる各移動のステップ サイズを制御します。

Train 関数をカプセル化する

巡回計算プロセスをトレーニング関数と更新関数にカプセル化する

実装ロジック: 出力を順方向に計算、出力と実際の値に基づいて損失を計算、損失と入力に基づいて勾配を計算、勾配に従ってパラメーター値を更新

class Network():
    def __init__(self,num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights,1)
        self.b = 0
        
    def forward(self,x):
        # x -> (1,13) , w-> (13,1)
        z = np.dot(x,self.w) + self.b
        return z
    def loss(self,z,y):
        error = z-y
        loss = np.mean(error * error)
        return loss
    def gradient(self,x,y):
        z = self.forward(x)
        gradient_w = (z-y)*x
        gradient_w = np.mean(gradient_w,axis=0)
        gradient_w = gradient_w[:,np.newaxis]
        
        gradient_b = (z-y)
        gradient_b = np.mean(gradient_b)
        return gradient_w,gradient_b
   def update(self,gradient_w,gradient_b,eta=0.01):
    	self.w = self.w - eta*gradient_w
        self.b = self.b - eta*gradient_b
   def train(self,x,y,iterations=100,eta = 0.01):
    	losses = []
        for i in range(iterations):
            z = self.forward(x)
            L = self.loss(z,y)
            gradient_w, gradient_b = self.gradient(x,y)
            self.update(gradient_w,gradient_b,eta)
            losses.append(L)
            if (i+1)%10 == 0:
                print("iter {},loss {}".format(i,L))
        return losses
    
# 获取数据
train_data ,test_data = load_data()
x = train_data[:,:-1]
y = train_data[:,-1:]
# 创建网络
net = Network(13)
num_iterations = 1000
# 启动训练
losses = net.train(x,y,iterations=num_iterations,eta=0.01)

# 画出损失函数的变化趋势
plot_x = np.arange(num_iterations)
plot_y = np.array(losses)
plt.plot(plot_x,plot_y)
plt.show()
2. 確率的勾配降下法

勾配降下法では、各損失関数と勾配計算はデータセットの全データに基づいていますが、実際の問題では、データセットが非常に大きくなることが多く、毎回全データを計算に使用すると、 、効率は非常に低いです。パラメーターはグラデーションの反対方向に沿って一度に少しだけ更新されるため、方向はそれほど正確である必要はありません。合理的な解決策は、全体を表すために毎回全データセットからデータの小さな部分をランダムに抽出し、データのこの部分に基づいて勾配と損失を計算してパラメータを更新することです. この方法は確率的勾配降下法と呼ばれます. (確率的勾配降下法、SGD)

コアアイデア

  • ミニバッチ: 各反復で抽出されたデータのバッチは、ミニバッチと呼ばれます
  • バッチサイズ: 各ミニバッチに含まれるサンプルの数は、バッチサイズと呼ばれます
  • エポック プログラムが反復するとき、サンプルはミニバッチに従って徐々に抽出されます. データセット全体がトラバースされると、エポック (ラウンド) とも呼ばれるトレーニングのラウンドが完了します. トレーニングが開始されると、トレーニングラウンドは次のようになります。 num_epochs と batch_size はパラメーターとして渡されます

train_data を batch_size の複数のミニバッチに分割します

batch_size = 10
n = train_data.shape[0]
np.random.shuffle(train_data)
mini_batches = [train_data[k:k+batch_size] for k in range(0,n,batch_size)]

SGD では、サンプルの一部をランダムに選択して全体を表現します. ランダム サンプリングの効果を得るために、最初に train_data 内のサンプルの順序をランダムに乱してから、ミニバッチを抽出します.

実験により、モデルは最後のデータに感銘を受けることがわかりました.トレーニングデータがインポートされた後、モデルトレーニングの終わりに近づくほど、モデルパラメータに対する最後のデータバッチの影響が大きくなります.防止するためにモデル メモリがトレーニング効果に影響を与えるのを防ぐため、サンプル操作の順序を変更する必要があります。

トレーニング プロセスでは、ランダムに選択された各ミニバッチ データがモデル トレーニング用のモデルに入力されます。トレーニング プロセスのコアは、2 層ループです。

  • ループの最初のレイヤーは、エポックと呼ばれる、サンプル セットがトレーニングされる回数を表します。

    for epoch_id in range(num_epochs):
        pass
    
  • ループの 2 番目の層は、各トラバーサル中にサンプル セットが分割される複数のバッチを表し、反復 (反復) と呼ばれるすべてのトレーニングを実行する必要があります。

    for iter_id,mini_batch in emumerate(mini_batches):
        pass
    

    内側のループは、古典的な 4 ステップのトレーニング プロセスです。

    1. 前方計算
    2. 損失を計算する
    3. 勾配を計算する
    4. パラメータの更新
def train(self,training_data,num_epochs,batch_size=10,eta=0.01):
    n= len(training_data)
    losses = []
    for epoch_id in range(num_epochs):
        # 在每轮迭代开始之前,将训练数据的顺序进行随机打乱
        # 然后再按照每次取出batch_size条数据的方式取出
        np.random.shuffle(training_data)
        # 将训练数据进行拆分,每个mini_batch包含batch_size条数据
        mini_batches = [training_data[k:k+batch_size] for k in range(0,n,batch_size)]
        for iter_id,mini_batch in enumerate(mini_batches):
            x = mini_batch[:,:-1]
            y = mini_batch[:,-1:]
            a = self.forward(x)
            loss = self.loss(a,y)
            gradient_w, gradient_b = self.gradient(x,y)
            self.update(gradient_w,gradient_b,eta)
            losses.append(loss)
            print("Epoch {:3d} / iter {:3d},loss = {:.4f}".format(epoch_id,iter_id,loss))
      return losses

確率的勾配降下法はトレーニング プロセスを高速化しますが、パラメーターを更新し、毎回少数のサンプルに基づいて損失を計算するだけなので、損失降下曲線が振動します。

ニューラル ネットワークでモデルを構築するための 3 つのポイント

  • ネットワークを構築してパラメータを初期化wwwbbb予測と損失関数の計算方法を定義する
  • 初期点をランダムに選択し、勾配計算方法とパラメータ更新方法を確立します
  • データセットのデータをbatch_sizeの大きさに合わせて複数のミニバッチに分割し、モデルに入れて勾配を計算してパラメータを更新し、損失関数がほとんど落ちなくなるまで繰り返します。

完全なコード

https://download.csdn.net/download/first_bug/87733907

import numpy as np
from matplotlib import pyplot as plt

def load_data():
    datafile = "./housing.data"
    data = np.fromfile(datafile,sep=" ")

    feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE','DIS', 
                 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]
    feature_num = len(feature_names)
    data = data.reshape([data.shape[0] // feature_num, feature_num])


    ratio = 0.8
    offset = int(data.shape[0] * ratio)
    training_data = data[:offset]
    maximums, minimums = training_data.max(axis=0), training_data.min(axis=0)

    # 对数据进行归一化处理,使用训练集的极值
    for i in range(feature_num):
        data[:, i] = (data[:, i] - minimums[i]) / (maximums[i] - minimums[i])

    # 训练集和测试集的划分
    training_data = data[:offset]
    test_data = data[offset:]
    return training_data,test_data

class Network():
    def __init__(self,num_of_weights):
        # 随机产生w的初始值
        # 为了保持程序每次运行结果的一致性,设置固定的随机数种子
        np.random.seed(0)
        self.w = np.random.randn(num_of_weights,1)
        self.b = 0
        
    def forward(self,x):
        # x -> (1,13) , w-> (13,1)
        z = np.dot(x,self.w) + self.b
        return z
    
    def loss(self,z,y):
        error = z-y
        loss = np.mean(error * error)
        return loss
    
    def gradient(self,x,y):
        z = self.forward(x)
        gradient_w = (z-y)*x
        gradient_w = np.mean(gradient_w,axis=0)
        gradient_w = gradient_w[:,np.newaxis]
        
        gradient_b = (z-y)
        gradient_b = np.mean(gradient_b)
        return gradient_w,gradient_b
    
    def update(self,gradient_w,gradient_b,eta=0.01):
        self.w = self.w - eta*gradient_w
        self.b = self.b - eta*gradient_b

    def train_gd(self,x,y,iterations=100,eta = 0.01):
        losses = []
        for i in range(iterations):
            z = self.forward(x)
            L = self.loss(z,y)
            gradient_w, gradient_b = self.gradient(x,y)
            self.update(gradient_w,gradient_b,eta)
            losses.append(L)
            if (i+1)%10 == 0:
                print("iter {},loss {}".format(i,L))
        return losses
    
    def train_sgd(self,training_data,num_epochs,batch_size=10,eta=0.01):
        n= len(training_data)
        losses = []
        for epoch_id in range(num_epochs):
            # 在每轮迭代开始之前,将训练数据的顺序进行随机打乱
            # 然后再按照每次取出batch_size条数据的方式取出
            np.random.shuffle(training_data)
            # 将训练数据进行拆分,每个mini_batch包含batch_size条数据
            mini_batches = [training_data[k:k+batch_size] for k in range(0,n,batch_size)]
            for iter_id,mini_batch in enumerate(mini_batches):
                x = mini_batch[:,:-1]
                y = mini_batch[:,-1:]
                a = self.forward(x)
                loss = self.loss(a,y)
                gradient_w, gradient_b = self.gradient(x,y)
                self.update(gradient_w,gradient_b,eta)
                losses.append(loss)
                print("Epoch {:3d} / iter {:3d},loss = {:.4f}".format(epoch_id,iter_id,loss))
        return losses
    def valid(self,test_data):
        x = test_data[:,:-1]
        y = test_data[:,-1:]
        a = self.forward(x)
        loss = self.loss(a,y)
        return loss

def main():
    # 获取数据
    train_data ,test_data = load_data()
    x = train_data[:,:-1]
    y = train_data[:,-1:]
    # 创建网络
    net = Network(13)
    num_iterations = 1000
    # 启动训练
    losses = net.train_gd(x,y,iterations=num_iterations,eta=0.01)
    #losses = net.train_sgd(train_data,num_iterations,batch_size=10,eta=0.01)
    
    print("valid loss: {:.4f}".format(net.valid(test_data)))

    # 画出损失函数的变化趋势
    plot_x = np.arange(len(losses))
    plot_y = np.array(losses)
    plt.plot(plot_x,plot_y)
    plt.show()

if __name__ == "__main__":
    main()

numpy 関数

np.fromfile

np.fromfile(file,dtype=float,count=-1,sep="",offset=0,like=None)

テキスト ファイルまたはバイナリ ファイルのデータから配列を作成し、既知のデータ型でバイナリ データを読み取り、単純な形式のテキスト ファイルを解析するための効率的な方法。

  • ファイル

    ファイル オブジェクトまたはファイル パスを開く

  • dtype

    配列のデータ型を返します。バイナリ ファイルの場合、ファイル内の項目のサイズとバイト順を決定するために使用されます。ほとんどの組み込みデータ型がサポートされています。

  • カウント: 整数

    読み取るアイテムの数、完全なファイルの場合は -1

  • 9月: str

    ファイルがテキスト ファイルの場合、項目間の区切り文字を指定します。空の ("") 区切り文字は、ファイルがバイナリと見なされることを意味します。区切り文字のスペース (" ") は、0 個以上の空白文字に一致します。

  • オフセット: 整数

    ファイルの現在の場所からのバイト単位のオフセット。デフォルトは 0 で、バイナリ ファイルにのみ許可されます。

  • like: array_like

    Numpy 配列ではない配列参照オブジェクトの作成を許可します. like として渡された配列のようなものが __array_function__ プロトコルをサポートしている場合, 結果はそれによって定義されます. この場合, と互換性のあるオブジェクトを作成することが保証されます.このパラメータを介して渡されたオブジェクト. 配列オブジェクト.

ndarray.shape

行列の次元情報 (0 次元の長さ、1 次元の長さ、...、n 次元の長さ) を返します。

2 行 3 列の行列 (2,3)

ndarray.reshape

マトリックスの次元情報を変更します。パラメータは次元情報です

同じデータを指す新しい ndarray オブジェクトを返します。1 つの ndarray を変更すると、データを指す他の ndarray オブジェクトも変更されます

numpy.dot

配列操作は要素レベルであり、配列を乗算した結果は、対応する各要素の積で構成される配列になります。

行列の場合、numpy は行列乗算用のドット関数を提供します

res = np.dot(a,b,out=None)

2 つの要素 a、b の積を取得します。

派手な乱数

乱数シードの設定

np.random.seed(n)

乱数を生成する

np.random.rand(d0,d1,d2,...,dn)

服従のセットを返す0 1 0~10 1 均一に分布するランダム サンプル値、サンプルの値の範囲は[ 0 , 1 ) [0,1)[ 0 ,1 )、1 を除く

np.random.randn(d0,d1,d2,...,dn)

標準正規分布に従う一連のランダム サンプル値を返します。値は基本的に− 1.96 ∼ + 1.96 -1.96 \thicksim + 1.96の間です1.96+ 1.96、値が大きいほど確率は小さくなります。

np.newaxis

np.newaxis の機能は新しい次元を追加することであり、位置が異なると生成される長方形の形状が異なります。

np.newaxis が配置されている場所に、その位置にディメンションが追加されます

  • x[:,np.newaxis]、後ろに配置すると、列に次元が追加されます
  • x[np.newaxis,:]を前に配置すると、列に次元が追加されます

これは通常、1 次元データを他の行列と乗算できる行列に変換するために使用されます。

np.random.shuffle

元の配列に対してシャッフル操作を実行し、要素を並べ替えて、元の順序をシャッフルします。

matplotlib 3D プロット

  1. 3 次元の座標軸オブジェクトを作成する Axes3D

    # 方法一,利用参数projection='3d'
    from matplotlib import pyplot as plt
    
    # 定义坐标轴
    fig = plt.figure()
    ax1 = plt.axes(projection="3d")
    
    # 方法二: 利用三维轴 ( 已废弃 )
    from matplotlib import pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    
    # 定义图像和三维格式坐标轴
    fig = plt.figure()
    ax2 = Axes3D(fig)
    
  2. 描く

    import numpy as np
    from matplotlib import pyplot as plt
    # 定义坐标轴
    fig = plt.figure()
    ax1 = plt.axes(projection="3d")
    
    # 设置xyz方向的变量
    z = np.linspace(0,13,1000) # 在[0-13]之间等距取1000个点
    x = 5*np.sin(z)
    y = 5*np.cos(z)
    
    #设置坐标轴
    ax1.set_xlabel('X')
    ax1.set_ylabel('Y')
    ax1.set_zlabel('Z')
    ax1.plot3D(x,y,z,'gray')#绘制空间曲线
    plt.show()#显示图像
    

    ここに画像の説明を挿入

    import numpy as np
    from matplotlib import pyplot as plt
    #定义新坐标轴
    fig=plt.figure()
    ax3=plt.axes(projection='3d')
     
    #定义三维数据
    xx=np.arange(-5,5,0.5)
    yy=np.arange(-5,5,0.5)
     
    #生成网格点坐标矩阵,对x和y数据执行网格化
    X,Y=np.meshgrid(xx,yy)
     
    #计算z轴数据
    Z=np.sin(X)+np.cos(Y)
     
    #绘图
    #函数plot_surface期望其输入结构为一个规则的二维网格
    ax3.plot_surface(X,Y,Z,cmap='rainbow') #cmap是颜色映射表
    plt.title("3D")
    plt.show()
    
    

    ここに画像の説明を挿入

ここに画像の説明を挿入

  • X、Y データが座標点を決定します
  • Z 軸データは、X および Y 座標点に対応する高さを決定します。

参考

https://aistudio.baidu.com/aistudio/projectdetail/5687440

おすすめ

転載: blog.csdn.net/first_bug/article/details/130396954