ハンズオンディープラーニング - pytorch バージョン (2): 線形ニューラル ネットワーク

参考文献

1. 線形ニューラルネットワーク

  • ニューラル ネットワークのトレーニング プロセス全体。これには、単純なニューラル ネットワーク アーキテクチャの定義、データ処理、損失関数の指定、およびモデルのトレーニング方法が含まれます。古典的な統計学習手法における線形回帰ソフトマックス回帰は、線形ニューラル ネットワークとみなすことができます。

1.1 線形回帰

  • 回帰は、1 つ以上の独立変数と従属変数の間の関係をモデル化できるメソッドのクラスです。自然科学や社会科学では、入力と出力の関係を表すために回帰がよく使用されます。
  • 機械学習の分野におけるほとんどのタスクは、通常、予測に関連しています。値を予測する場合には、回帰問題が関係します。一般的な例には、価格の予測 (住宅、株式など)、入院期間の予測 (入院患者など)、需要の予測 (小売売上高など) が含まれます。

1.1.1 線形回帰の基本要素

  • 線形回帰はいくつかの単純な仮定に基づいています

    • まず、独立変数xxを仮定します。xと従属変数yyy間の関係は線形です、つまりyyy はxxとして表現できますxの要素の加重合計
    • 第 2 に、通常、観測値にある程度のノイズを含めることが許可されており、ノイズが正規分布に従うなど、ノイズは比較的正規であると想定されます。
  • 実用的な例を挙げると、サイズ (平方フィート) と築年数 (年) に基づいて家の価格 (USD) を推定したいと考えています。

    • 住宅価格を予測できるモデルを開発するには、実際のデータセットを収集する必要があります
      • このデータセットには、住宅の販売価格、サイズ、築年数が含まれています
      • 機械学習の用語では、このデータセットはトレーニングセットと呼ばれます。
    • データの各行 (住宅取引に対応するデータなど) はサンプルと呼ばれ、データ ポイントまたはデータ インスタンスとも呼ばれます。
    • 予測しようとしているターゲット (住宅価格の予測など) をラベルまたはターゲットと呼びます。
    • 予測の基礎となる独立変数 (面積と年齢) は、特徴または共変量と呼ばれます。
線形モデル
  • 線形仮定とは、目標 (住宅価格) が特徴 (面積と築年数) の加重和として表現できることを意味します。
    価格 = wara ⋅ 面積 + 賃金 ⋅ 年齢 + b \mathrm{価格}=w_{\mathrm{面積} }\cdot \mathrm{面積}+w_{\mathrm{年齢}}\cdot\mathrm{年齢}+b価格=wエリアエリア+w年齢+b

    • _{エリア} を参照してくださいwある_ _ w a g e w_{age} wアゲ_ _重み (重み) と呼ばれ、重みは予測値に対する各特徴の影響を決定します。
    • bbbはバイアス(バイアス)、オフセット(オフセット)、または切片(インターセプト)と呼ばれます。バイアスとは、すべての特徴が 0 である場合の予測値を指しますバイアス項がないと、モデルの表現力が制限されます
    • 上の式は、入力特徴のアフィン変換です。アフィン変換は、重み付けとバイアス項による変換によって特徴を線形変換します。
  • データセットが与えられた場合、目標はモデルの重みwwを見つけることです。wとバイアスbbb、モデルによって行われた予測がデータ内の実際の価格とほぼ一致するようにします。出力の予測値は、線形モデルによる入力特徴のアフィン変換によって決定され、アフィン変換は選択された重みとバイアスによって決定されます。

  • 機械学習の分野では、入力にddが含まれる場合、通常、高次元のデータセットが使用されます。d 個の特徴がある場合、予測結果はy ^ \hat{y}y^( yyには「尖った」記号を使用してくださいy推定値)
    y ^ = w 1 x 1 + . . . + wdxd + b \hat{y}=w_1x_1+...+w_dx_d+b として表されます。y^=w1バツ1+...+wdバツd+b

  • すべての特徴をベクトルx ∈ R d {\mathbf{x}}\in\mathbb{R}^{d}に入れますバツRd を計算し、すべての重みをベクトルw ∈ R d {\mathbf{w}}\in\mathbb{R}^{d} にwRdでは、モデルy ^ = w ⊤ x + b \hat{y}=\mathbf{w}^\top\mathbf{x}+b は、内積の形式で簡潔に表現できます。
    y^=w ×+b

  • 上の式では、ベクトルx {\mathbf{x}}x は、単一のデータ サンプルの特徴に対応します。シンボリック行列X ∈ R n × d \mathbf{X}\in\mathbb{R}^{n\times d}バツRn × dは、データセット全体のnnを便利に参照できます。n個のサンプル。ここでX \mathbf{X}Xサンプルであり、各列は特徴です特徴セットX \mathbf{X}X、予測値y ^ ∈ R n \hat{\mathbf{y}}\in\mathbb{R}^{n}y^Rn は、行列とベクトルの乗算で次のように表すことができます。
    y ^ = X w + b \hat{\mathbf{y}}=\mathbf{X}\mathbf{w}+by^=Xw+b

  • トレーニング データの特徴が X \mathbf{X}であるとすると、Xおよび対応する既知のラベル yyy、線形回帰の目標は重みベクトルのセットを見つけることですwwwとバイアスbbb

    • X \mathbf{X}から与えられた場合新しいサンプルの特徴がXの同じ分布でサンプリングされる場合、この重みベクトルとオフセットのセットにより、新しいサンプルの予測ラベルの誤差を可能な限り小さくできます。
    • 特徴とラベルの間の基礎的な関係が線形であると確信している場合でも、観測誤差の影響を考慮してノイズ項が追加されます。

まずは最適なモデルパラメータ(モデルパラメータ)を探すwwwbbbの前に、さらに 2 つのことが必要です

  • (1)モデルの品質の尺度
  • (2)モデルを更新してモデル予測の品質を向上させる手法
損失関数
  • モデルをデータに適合させる方法を検討する前に、適合の尺度を特定する必要があります。損失関数は、ターゲットの実際の値と予測値の差を定量化できます通常、負ではない数値が損失として選択され、値が小さいほど損失は小さくなり、完全な予測では損失は 0 になります。回帰問題で最も一般的に使用される損失関数は、二乗誤差関数です。サンプルiiのときiの予測値はy ^ ( i ) \hat{y}^{(i)} です。y^( i )、それに対応する真のラベルはy ( i ) y^{(i)}y( i )の場合、二乗誤差は次の式で定義できます。
    l ( i ) ( w , b ) = 1 2 ( y ^ ( i ) − y ( i ) ) 2 l^{(i)}(\mathbf{ w} ,b)=\frac12\left(\hat{y}^{(i)}-y^{(i)}\right)^2( i ) (wb )=21(y^()y( i ) )2

  • 二乗誤差関数の二次項により、推定値y ^ ( i ) \hat{y}^{(i)}y^( i )と観測値y ( i ) y^{(i)}y( i )の差が大きいほど、データセット全体のシーンモデルの品質を測定するには、トレーニングセットnnn サンプルにわたる平均損失 (合計にも等しい) L
    ( w , b ) = 1 n ∑ i = 1 nl ( i ) ( w , b ) = 1 n ∑ i = 1 n 1 2 ( w ⊤ x ( i ) + b − y ( i ) ) 2 L(\mathbf{w},b)=\frac1n\sum_{i=1}^nl^{(i)}(\mathbf{w},b) =\frac1n\ sum_{i=1}^n\frac12\left(\mathbf{w}^\top\mathbf{x}^{(i)}+by^{(i)}\right)^2L ( w ,b )=n1i = 1( i ) (wb )=n1i = 121( w ×()+by( i ) )2

  • モデルをトレーニングするときは、パラメーターのセット( w ∗ , b ∗ ) (\mathbf{w}^*,b^*) を見つけたいと考えています。( wb )、このパラメーターのセットは総損失を最小限に抑えることができます
    w ∗ , b ∗ = argmin ⁡ w , b L ( w , b ) \mathbf{w}^*,b^*=\underset{\ mathbf{w}, b}{\演算子名*{argmin}}L(\mathbf{w},b)wb=w bアルグミンL ( w ,b )

分析ソリューション
  • 他のほとんどのモデルとは異なり、線形回帰の解は単純に数式で表すことができ、これを分析解と呼びます。まずはBBをバイアスしますbはパラメータw \mathbf{w}wでは、マージは、すべてのパラメーターを含む行列に列を追加することによって行われます。予測問題は、∥ y − X w ∥ 2 \|\mathbf{y}-\mathbf{X}\mathbf{w}\|^2 を最小はいXw これには、損失平面上に臨界点が 1 つだけあり、領域全体の損失最小値に対応します。約w \mathbf{w} を失いますwの導関数は 0 に設定され、解析解
    w ∗ = ( X ⊤ X ) − 1 X ⊤ y \mathbf{w}^*=(\mathbf{X}^\top\mathbf{X})^ {-1 }\mathbf{X}^\top\mathbf{y}w=( X⊤X )_1 ×⊤y _
確率的勾配降下法
  • 勾配降下法は、ほぼすべての深層学習モデルを最適化でき、損失関数が減少する方向にパラメータを継続的に更新することで誤差を低減します。

  • 通常、更新を計算する必要があるたびに、小さなバッチのサンプルがランダムに選択されます。これは、ミニバッチ確率的勾配降下法と呼ばれます。

    • 各反復では、まずミニバッチB \mathcal{B}をランダムにサンプリングします。B、固定数のトレーニング サンプルで構成されます。
    • 次に、モデル パラメーターに関するミニバッチの平均損失の導関数(勾配とも呼ばれます)を計算します。
    • 最後に、勾配に所定の正の数η \etaを掛けます。ηも不定詞
      w ← w − η ∣ B ∣ ∑ i ∈ B ∂ wl ( i ) ( w , b ) = w − η ∣ B ∣ ∑ i ∈ B x ( i ) ( w ⊤ x ( i ) + b − y ( i ) ) , b ← b − η ∣ B ∣ ∑ i ∈ B ∂ bl ( i ) ( w , b ) = b − η ∣ B ∣ ∑ i ∈ B ( w ⊤ x(i ) + b − y(i))。\begin{aligned}\mathbf{w}&\leftarrow\mathbf{w}-\frac\eta{|\mathcal{B}|}\sum_{i\in\mathcal{B}}\partial_\mathbf{w }l^{(i)}(\mathbf{w},b)=\mathbf{w}-\frac\eta{|\mathcal{B}|}\sum_{i\in\mathcal{B}}\ mathbf{x}^{(i)}\left(\mathbf{w}^\top\mathbf{x}^{(i)}+by^{(i)}\right),\\b&\leftarrow b -\fraction{|\mathcal{B}|}\sum_{i\in\mathcal{B}}\partial_bl^{(i)}(\mathbf{w},b)=b-\fraction {|\mathcal {B}|}\sum_{i\in\mathcal{B}}\left(\mathbf{w}^\top\mathbf{x}^{(i)}+by^{(i) }\right) .\end{整列}wbwB hi B( i ) (wb )=wB hi Bバツ()( w ×()+by( i ) )bB hi Bb( i ) (wb )=bB hi B( w ×()+by( i ) ).
  • B \数学{B}B は各ミニバッチ内のサンプル数を表し、バッチ サイズη\etaηは学習率(学習率)を表すバッチ サイズと学習率の値は通常、モデルのトレーニングを通じて取得されるのではなく、手動で事前に指定されます。

    • 調整可能だがトレーニング中に更新できないこれらのパラメーターは、ハイパーパラメーターと呼ばれます。ハイパーパラメータ調整は、ハイパーパラメータを選択するプロセスです
    • ハイパーパラメータは通常、独立した検証データセットで評価されるトレーニング反復の結果に基づいて調整されます。
モデルを使用して予測を行う
  • 「学習された」線形回帰モデルを仮定すると、w ^ ⊤ x + b ^ \mathbf{\hat{w}}^{\top}\mathbf{x}+\hat{b}w^ ×+b^、住宅エリアx 1 x_1バツ1および住宅築年数x 2 x_2バツ2新しい家の価格を推定するため (トレーニング データには含まれていません)。特徴を与えてターゲットを推定するプロセスは、多くの場合、予測または推論と呼ばれます。

1.1.2 ベクトル化の高速化

  • モデルをトレーニングするときは、サンプルのミニバッチ全体を一度に処理できることが望ましい場合が多く、これを実現するには、計算をベクトル化する必要があります。
  • ベクトル化の重要性を説明するために、すべて 1 の 2 つの 10,000 次元ベクトル をインスタンス化する、ベクトルを追加する 2 つの方法を考えてみましょう。
    • 1 つのアプローチでは、Python の for ループを使用してベクトルを反復処理します。
    • 別のアプローチでは、+ への呼び出しに依存します。
    import math
    import time
    import numpy as np
    import torch
    
    n = 10000
    a = torch.ones([n])
    b = torch.ones([n])
    
    # 定义一个计时器
    class Timer:
        def __init__(self):
            self.times = []
            self.start()
        def start(self):
            self.tik = time.time()
    
        def stop(self):
            self.times.append(time.time() - self.tik)
            return self.times[-1]
    
        def avg(self):
            return sum(self.times) / len(self.times)
    
        def sum(self):
            return sum(self.times)
    
        def cumsum(self):
            return np.array(self.times).cumsum().tolist()
    
    # 使用 for 循环,每次执行一位的加法
    c = torch.zeros(n)
    timer = Timer()
    for i in range(n):
        c[i] = a[i] + b[i]
    
    # 使用重载的 + 运算符来计算按元素的和
    # 矢量化代码通常会带来数量级的加速
    timer.start()
    d = a + b
    
    print(f'{
            
            timer.stop():.5f} sec')
    
    # 输出
    '0.20727 sec'
    '0.00020 sec'
    

1.1.3 正規分布と二乗損失

  • 二乗損失目的関数は、ノイズ分布に関する仮定を通じて解釈されます。正規分布 (normal distribution)、ガウス分布 (Gaussian distribution) とも呼ばれます: 確率変数xxの場合xの平均値はμ \muμと分散σ 2 \sigma^{2}p2 (標準偏差σ \sigmaσ )、その正規分布確率密度関数は次のとおりです
    p ( x ) = 1 2 π σ 2 exp ⁡ ( − 1 2 σ 2 ( x − μ ) 2 ) \begin{aligned}p(x)&=\frac1 { \sqrt{2\pi\sigma^2}}\exp\left(-\frac1{2\sigma^2}(x-\mu)^2\right)\end{aligned}p ( x )=2_2 1経験値(2P _21( ×メートル2 )

  • 平均二乗誤差損失関数 (略して平均二乗損失) を線形回帰に使用できる理由の 1 つは、観測値にノイズが含まれており、ノイズが正規分布に従うと仮定しているためです。ノイズの正規分布は次のとおりです。 ここで、ϵ 〜 N ( 0 , σ 2 ) \epsilon\sim\mathcal{N}(0,\sigma^2)ϵN ( 0 ,p2 )
    y = w ⊤ x + b + ϵ y=\mathbf{w}^\top\mathbf{x}+b+\epsilony=w ×+b+ϵ

  • したがって、与えられたx \mathbf{x}に対して次のように書くことができるようになりました。x は特定のyyyの尤度
    P ( y ∣ x ) = 1 2 π σ 2 exp ⁡ ( − 1 2 σ 2 ( y − w ⊤ x − b ) 2 ) P(y\mid\mathbf{x})= \frac1{ \sqrt{2\pi\sigma^2}}\exp\left(-\frac1{2\sigma^2}(y-\mathbf{w}^\top\mathbf{x}-b)^ 2\right )P (× =2_2 1経験値(2P _21( yw ×b )2 )

  • さて、最尤推定法によれば、パラメータw \mathbf{w}wbbbの最適値は、データセット全体の尤度を最大化する値
    です。 P ( y ∣ X ) = ∏ i = 1 np ( y ( i ) ∣ x ( i ) ) P(\mathbf{y}\mid\ mathbf {X})=\prod_{i=1}^np(y^{(i)}|\mathbf{x}^{(i)})P (× )=i = 1p (( i )x( i ) )

− log ⁡ P ( y ∣ X ) = ∑ i = 1 n 1 2 log ⁡ ( 2 π σ 2 ) + 1 2 σ 2 ( y ( i ) − w ⊤ x ( i ) − b ) 2 -\log P (\mathbf{y}\mid\mathbf{X})=\sum_{i=1}^n\frac12\log(2\pi\sigma^2)+\frac1{2\sigma^2}\left( y^{(i)}-\mathbf{w}^\top\mathbf{x}^{(i)}-b\right)^2ログ_P (× )=i = 121log ( 2 π σ _2 )+2P _21( y()w ×()b )2

import math
import numpy as np
import matplotlib.pyplot as plt

def normal(x, mu, sigma):
    p = 1 / math.sqrt(2 * math.pi * sigma**2)
    return p * np.exp(-0.5 / sigma ** 2 * (x - mu) ** 2)

x = np.arange(-7, 7, 0.01)

# 改变均值会产生沿 x 轴的偏移,增加方差将会分散分布、降低峰值
params = [(0, 1), (0, 2), (3, 1)]
plt.figure(figsize=(8, 6))
for mu, sigma in params:
    plt.plot(x, normal(x, mu, sigma), label=f'mean {
      
      mu}, std {
      
      sigma}')

plt.xlabel('x')
plt.ylabel('p(x)')
plt.legend()
plt.show()

ここに画像の説明を挿入

1.1.4 線形回帰からディープネットワークへ

ニューラルネットワーク図
  • 以下に示すニューラルネットワークでは

    • 入力はx 1 , … , xd x_{1},\ldots,x_{d}ですバツ1バツdしたがって、入力層の入力 (または特徴次元)の数はddです。d
    • ネットワークの出力はo 1 o_1ですああ1、したがって、出力層の出力の数は 1 です。
  • 入力値はすべて与えられており、計算ニューロンは 1 つだけです。モデルの焦点は計算が行われる場所であるため、通常、レイヤー数を計算する際に入力レイヤーは考慮されません。つまり、下図のニューラルネットワークの層数は1です。

  • 線形回帰モデルは、単一の人工ニューロンのみで構成されるニューラル ネットワーク、または単層ニューラル ネットワークと考えることができます線形回帰の場合、各入力は各出力に接続されます (この場合、出力は 1 つだけです)。この変換 (図の出力層) は全結合層 (全結合層) または全結合層と呼ばれます。緻密層(濃海苔)

ここに画像の説明を挿入

1.2 線形回帰の簡単な実装

1.2.1 データセットの生成

  • 1000 個のサンプルのデータセットを生成します。各サンプルには、標準正規分布からサンプリングされた 2 つの特徴が含まれます。合成データセットは行列x ∈ R 1000 × 2 \mathbf{x}\in\mathbb{R}^{1000\times2}です。バツR1000 × 2
  • 線形モデル パラメーターを使用するw = [ 2 , − 3.4 ] T , b = 4.2 \mathbf{w}=[2,-3.4]^{\mathsf{T}},b=4.2w=[ 2 3.4 ]b=4.2とノイズ項ϵ \epsilonϵデータセットとそのラベルを 生成する
    • ϵ \イプシロンϵ \epsilonを仮定すると、 ϵはモデルの予測とラベル付けにおける潜在的な観測誤差とみなすことができます。ϵ は平均 0 の正規分布に従い、標準偏差は 0.01 に設定されます。
      y = X w + b + ϵ \mathbf{y}=\mathbf{X}\mathbf{w}+b+\epsilony=Xw+b+ϵ
    import numpy as np
    import torch
    from torch.utils import data
    
    def synthetic_data(w, b, num_examples):
        X = torch.normal(0, 1, (num_examples, len(w)))
        y = torch.matmul(X, w) + b
        y += torch.normal(0, 0.01, y.shape)
        return X, y.reshape((-1, 1))
    
    true_w = torch.tensor([2, -3.4])
    true_b = 4.2
    features, labels = synthetic_data(true_w, true_b, 1000)
    

1.2.2 データセットの読み取り

  • フレームワーク内の既存の API を呼び出してデータを読み取ります。機能とラベルを API のパラメーターとして渡し、データ イテレーターを介して batch_size を指定します。さらに、ブール値 is_train は、データ反復子オブジェクトが反復サイクルごとにデータをシャッフルするかどうかを示します。
    def load_array(data_arrays, batch_size, is_train=True):
        dataset = data.TensorDataset(*data_arrays)
        return data.DataLoader(dataset, batch_size, shuffle=is_train)
    
    batch_size = 10
    data_iter = load_array((features, labels), batch_size)
    
    # 为了验证是否正常工作,读取并打印第一个小批量样本
    # 使用 iter 构造 Python 迭代器,并使用 next 从迭代器中获取第一项
    print(next(iter(data_iter)))
    
    # 输出
    [tensor([[ 1.0829, -0.0883],
            [ 0.0989,  0.7460],
            [ 1.0245, -0.1956],
            [-0.7932,  1.7843],
            [ 1.2336,  1.0276],
            [ 2.1166,  0.2072],
            [-0.1430,  0.4944],
            [ 0.7086,  0.3950],
            [-0.0851,  1.4635],
            [ 0.2977,  1.8625]]), 
    tensor([[ 6.6616],
            [ 1.8494],
            [ 6.9229],
            [-3.4516],
            [ 3.1747],
            [ 7.7283],
            [ 2.2302],
            [ 4.2612],
            [-0.9383],
            [-1.5352]])]
    

1.2.3 モデルの定義

  • 標準の深層学習モデルの場合、フレームワークの事前定義されたレイヤーを使用できます。モデルの構築にどのレイヤーが使用されるかのみに注目する必要があり、レイヤーの実装の詳細に注意を払う必要はありません。
  • PyTorch では、全結合層は Linear クラスで定義されます。2 つのパラメータを nn.Linear に渡すことに注意してください。
    • 最初は入力フィーチャの形状を指定します。これは 2 です。
    • 2 番目は出力フィーチャの形状 (単一スカラー) を指定します。これは 1 です。
    from torch import nn
    
    net = nn.Sequential(nn.Linear(2, 1))
    

1.2.4 モデルパラメータの初期化

  • 線形回帰モデルの重みやバイアスなどのモデル パラメーターは、ネットを使用する前に初期化する必要があります。深層学習フレームワークには通常、パラメーターを初期化するための事前定義されたメソッドがあります。ここでは、各重みパラメータが平均 0、標準偏差 0.01 の正規分布からランダムにサンプリングされるように指定されており、バイアス パラメータは 0 に初期化されます。
    net[0].weight.data.normal_(0, 0.01)
    net[0].bias.data.fill_(0)
    

1.2.5 損失関数の定義

  • 平均二乗誤差は、MSELoss クラス (二乗L 2 L_2とも呼ばれます) を使用して計算されます。L2標準。デフォルトでは、すべてのサンプル損失の平均を返します。
    loss = nn.MSELoss()
    

1.2.6 最適化アルゴリズムを定義する

  • ミニバッチ確率的勾配降下法アルゴリズムは、ニューラル ネットワークを最適化するための標準ツールであり、PyTorch はこのアルゴリズムの多くのバリアントを optim モジュールに実装しています。SGD インスタンスをインスタンス化するときは、最適化アルゴリズムに必要な最適化されたパラメータとハイパーパラメータのディクショナリを指定します。小規模バッチの確率的勾配降下法では、lr 値を設定するだけで済みます。ここでは 0.03 に設定されています。
    trainer = torch.optim.SGD(net.parameters(), lr=0.03)
    

1.2.7 トレーニング

  • 各反復サイクルでは、データ セットが 1 回完全に走査され、入力と対応するラベルの小さなバッチがそこから継続的に取得されます。ミニバッチごとに、次の手順を実行します。
    • net(X) (フォワードパス)を呼び出して予測を生成し、損失 l を計算します。
    • 勾配はバックプロパゲーションを実行して計算されます
    • オプティマイザーを呼び出しモデルパラメータを更新します
  • トレーニング効果をより適切に測定するには、各反復サイクル後の損失を計算し、それを印刷してトレーニング プロセスを監視します。
    num_epochs = 3
    for epoch in range(num_epochs):
        for X, y in data_iter:
            l = loss(net(X) ,y)
            trainer.zero_grad()
            l.backward()
            trainer.step()
        l = loss(net(features), labels)
        print(f'epoch {
            
            epoch + 1}, loss {
            
            l:f}')
    
  • コードの概要
    import numpy as np
    import torch
    from torch.utils import data
    from torch import nn
    
    # 生成数据集
    def synthetic_data(w, b, num_examples):
        X = torch.normal(0, 1, (num_examples, len(w)))
        y = torch.matmul(X, w) + b
        y += torch.normal(0, 0.01, y.shape)
        return X, y.reshape((-1, 1))
    
    true_w = torch.tensor([2, -3.4])
    true_b = 4.2
    features, labels = synthetic_data(true_w, true_b, 1000)
    
    # 读取数据集
    def load_array(data_arrays, batch_size, is_train=True):
        dataset = data.TensorDataset(*data_arrays)
        return data.DataLoader(dataset, batch_size, shuffle=is_train)
    
    batch_size = 10
    data_iter = load_array((features, labels), batch_size)
    
    # 定义模型
    net = nn.Sequential(nn.Linear(2, 1))
    
    # 初始化模型参数
    net[0].weight.data.normal_(0, 0.01)
    net[0].bias.data.fill_(0)
    
    # 定义损失函数
    loss = nn.MSELoss()
    
    # 定义优化算法
    trainer = torch.optim.SGD(net.parameters(), lr=0.03)
    
    # 训练
    num_epochs = 3
    for epoch in range(num_epochs):
        for X, y in data_iter:
            l = loss(net(X) ,y)
            trainer.zero_grad()
            l.backward()
            trainer.step()
        l = loss(net(features), labels)
        print(f'epoch {
            
            epoch + 1}, loss {
            
            l:f}')
    
    w = net[0].weight.data
    print('w的估计误差:', true_w - w.reshape(true_w.shape))
    b = net[0].bias.data
    print('b的估计误差:', true_b - b)
    
    # 输出
    epoch 1, loss 0.000216
    epoch 2, loss 0.000104
    epoch 3, loss 0.000102
    w的估计误差: tensor([-0.0002,  0.0004])
    b的估计误差: tensor([0.0002])
    

1.3 ソフトマックス回帰

1.3.1 分類問題

  • 画像分類の問題から始めます。各入力が 2 x 2 のグレースケール画像であると仮定します。各ピクセル値はスカラーで表すことができ、各画像は 4 つの特徴x 1 、 x 2 、 x 3 、 x 4 x_{1}、x_{2}、x_{3}、x_{4}に対応します。バツ1バツ2バツ3バツ4また、各画像は「猫」「鶏」「犬」のカテゴリのいずれかに属するものとする。
  • カテゴリカル データを表す簡単な方法:ワンホット エンコーディングワンホット エンコーディングは、クラスと同じ数のコンポーネントを持つベクトルです。カテゴリに対応するコンポーネントは 1 に設定され、他のすべてのコンポーネントは 0 に設定されます。この例では、ラベルyyy は3 次元ベクトルになります。(1,0,0) は「猫」、(0,1,0) は「鶏」、(0,0,1) は「犬」に対応します。 y ∈ { (
    1 , 0 , 0 ) , ( 0 , 1 , 0 ) , ( 0 , 0 , 1 ) } y\in\{(1,0,0),(0,1,0),(0,0,1 ) \}y{( 1 ,0 0 ) ( 0 ,1 0 ) ( 0 ,0 1 )}

1.3.2 ネットワークアーキテクチャ

  • 考えられるすべてのクラスの条件付き確率を推定するには、クラスごとに 1 つずつ、複数の出力を持つモデルが必要です。線形モデルを使用して分類問題を解決するには、出力と同じ数のアフィン関数が必要です。各出力は独自のアフィン関数に対応します。この例では、4 つの特徴と 3 つの可能な出力クラスがあるため、重みを表すには 12 個のスカラーが必要になります (添え字付きwww )、バイアスを表す 3 つのスカラー (bbb)。以下は、入力ごとに 3 つの非正規化予測 (ロジット) を計算します:o 1 、o 2 および o 3 o_1,o_2\text{and}o_3ああ1ああ2そして、ああ3
    o 1 = x 1 w 11 + x 2 w 12 + x 3 w 13 + x 4 w 14 + b 1 、o 2 = x 1 w 21 + x 2 w 22 + x 3 w 23 + x 4 w 24 + b 2、o 3 = x 1 w 31 + x 2 w 32 + x 3 w 33 + x 4 w 34 + b 3 。\begin{aligned}o_1&=x_1w_{11}+x_2w_{12}+x_3w_{13}+x_4w_{14}+b_1,\\o_2&=x_1w_{21}+x_2w_{22}+x_3w_{23}+x_4w_{ 24}+b_2,\\o_3&=x_1w_{31}+x_2w_{32}+x_3w_{33}+x_4w_{34}+b_3.\end{整列}ああ1ああ2ああ3=バツ1w11+バツ2w12+バツ3w13+バツ4w14+b1=バツ1w21+バツ2w22+バツ3w23+バツ4w24+b2=バツ1w31+バツ2w32+バツ3w33+バツ4w34+b3.

  • この計算プロセスはニューラル ネットワーク図で説明できます。線形回帰と同様に、ソフトマックス回帰も各出力o 1 、o 2 および o 3 o_1,o_2\text{and}o_3 の計算により単層ニューラル ネットワークです。ああ1ああ2そして、ああ3すべての入力x 1 、 x 2 、 x 3 、 x 4 に依存します x_{1},x_{2},x_{3}\text{and}x_{4}バツ1バツ2バツ3そして×4したがって、ソフトマックス回帰の出力層も全結合層になります。

ここに画像の説明を挿入

1.3.3 全結合層のパラメータオーバーヘッド

  • 完全に接続された層は「完全に」接続されており、多くの学習可能なパラメータを持つ可能性があります。具体的には、どのような場合でも、d入力とqqq 個の出力を持つ全結合層O ( dq ) \mathcal{O}(dq)O ( d q )追加しますd入力はqqに変換されますq個の出力のコストは O ( dqn ) \mathcal{O}({\frac{dq}{n}})に削減できますnd q)、ここでハイパーパラメータnnn は、実際のアプリケーションにおけるパラメータの保存とモデ​​ルの有効性のバランスを取るために柔軟に指定できます。

1.3.4 ソフトマックス演算

  • Softmax 関数は、モデルを微分可能に保ちながら、正規化されていない予測を合計が 1 になる非負の数に変換します。これを達成するには、正規化されていない各予測が最初にべき乗され、出力が負でないことが保証されます。最終出力の確率値の合計が 1 になるようにするには、各べき乗の結果をその合計で除算します。
    y ^ = Softmax ( o ) ここで、 y ^ j = exp ⁡ ( oj ) ∑ k exp ⁡ ( ok ) \ hat{\mathbf{y}}=\mathrm{softmax}(\mathbf{o})\quad\text{where}\quad\hat{y}_j=\frac{\exp(o_j )}{\sum_k\ exp(o_k)}y^=ソフトマックス( o )y^j=exp ( o)exp ( oj)

  • ソフトマックスは非線形関数ですが、ソフトマックス回帰の出力は入力特徴のアフィン変換によって決まります。したがって、ソフトマックス回帰は線形モデルです

1.3.5 ミニバッチサンプルのベクトル化

  • 計算効率を向上させ、GPU を最大限に活用するために、通常、ベクトル計算はサンプルのミニバッチのデータに対して実行されます。ソフトマックス回帰のベクトル計算式は
    O = XW + b , Y ^ = Softmax ( O ) \begin{aligned}\mathbf{O}&=\mathbf{X}\mathbf{W}+\mathbf{b}, \\\hat{\mathbf{Y}}&=\mathrm{softmax}(\mathbf{O})\end{aligned}Y^=XW+b =ソフトマックス( O )

1.3.6 損失関数

  • わずかに、基本的に同じ線形回帰

1.3.7 情報理論の基礎

  • 情報理論は、情報やデータのエンコード、デコード、送信、処理を可能な限り簡潔に扱います。

  • 情報理論の中心的な考え方は、データ内の情報内容を定量化することであり、この値は分布PPと呼ばれますP(エントロピー)
    H [ P ] = ∑ j − P ( j ) log ⁡ P ( j ) H[P]=\sum_j-P(j)\log P(j)H [ P ]=jP ( j )ログ_P ( j )

1.3.8 モデルの予測と評価

  • ソフトマックス回帰モデルをトレーニングした後、任意のサンプル特徴を与えて、各出力クラスの確率を予測できます。通常、予測確率が最も高いクラスが出力クラスとして使用されます実際のクラス (ラベル) と一致する場合、予測は正しいです。モデルのパフォーマンスは、正しい予測の数と予測の総数の比率に等しい精度を使用して評価されます。

1.4 画像分類データセット

1.4.1 データセットの読み取り

import torch
import torchvision
from torch.utils import data
from torchvision import transforms
import matplotlib.pyplot as plt

# 通过 ToTensor 实例将图像数据从 PIL 类型变换成 32 位浮点数格式
# 并除以 255 使得所有像素的数值均在 0~1 之间
trans = transforms.ToTensor()
# root:指定数据集下载或保存的路径;train:指定加载的是训练数据集还是测试数据集
# transform:指定数据集的转换操作;download:指定是否下载数据集
mnist_train = torchvision.datasets.FashionMNIST(
    root="./data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST(
    root="./data", train=False, transform=trans, download=True)

# 将标签转换成对应的类别名称
def get_fashion_mnist_labels(labels):
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    # 这是一个列表推导式
        # 1.将 labels 中的每个元素按照索引转换为对应的文本标签
        # 2.然后将这些元素组成一个新的列表并返回
    return [text_labels[int(i)] for i in labels]

def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5):
    figsize = (num_cols * scale, num_rows * scale)
    # 第一个变量_是一个通用变量名,通常用于表示一个不需要使用的值
    # 第二个变量 axes 是一个包含所有子图对象的数组
    # 这里使用这种命名约定是为了表示只关心 axes 而不关心第一个返回值
    _, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
    axes = axes.flatten()  # 将 axes 展平为一维数组
    # 遍历 axes 和 imgs 的元素,其中 i 为索引,ax 为当前子图,img 为当前图像
    for i, (ax, img) in enumerate(zip(axes, imgs)):
        if isinstance(img, torch.Tensor):  # img 是一个 torch.Tensor 类型
            # img 是一个张量,假设其形状为 (C, H, W),其中 C 代表通道数,H 代表高度,W 代表宽度
            # permute(1, 2, 0) 是对 img 进行维度重排操作。它将维度从 (C, H, W) 重排为 (H, W, C)
            ax.imshow(img.permute(1, 2, 0))
        else:
            ax.imshow(img) 
        ax.axis('off')  # 关闭图像的坐标轴
        if titles:
            ax.set_title(titles[i])
    plt.show()

X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
show_images(X, 2, 9, titles=get_fashion_mnist_labels(y))

ここに画像の説明を挿入

1.4.2 小さなバッチの読み取り

  • トレーニング セットとテスト セットを読みやすくするために、最初から作成するのではなく、組み込みのデータ イテレーターを使用します。各反復で、データ ローダーは毎回、batch_size のサイズの小さなデータ バッチを読み取ります。すべてのサンプルをランダムにシャッフルする組み込みデータ反復子によるミニバッチの不偏読み取り
    • より大きなデータセットを扱う場合、ネットワークに一度にフィードしても良好なトレーニング結果は得られません。通常、サンプル全体の数は複数のバッチに分割され、各バッチ内のサンプルの数はサンプル サイズbatch_sizeと呼ばれます。
    batch_size = 256
    
    def get_dataloader_workers():
        return 4  # 使用 4 个进程来读取数据
    train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, 
                                 num_workers=get_dataloader_workers())
    

1.4.3 すべてのコンポーネントの統合

  • ここで、Fashion-MNIST データセットをフェッチして読み取るために、load_data_fashion_mnist 関数を定義します。この関数は、トレーニング セットと検証セットのデータ イテレータを返しますさらに、この関数は、画像を別の形状にサイズ変更するために使用されるオプションのパラメーター サイズも受け入れます。
    def load_data_fashion_mnist(batch_size, resize=None):
        # 下载 Fashion-MNIST 数据集,然后将其加载到内存中
        trans = [transforms.ToTensor()]
        if resize:
            trans.insert(0, transforms.Resize(resize))
        trans = transforms.Compose(trans)
        mnist_train = torchvision.datasets.FashionMNIST(
            root="./data", train=True, transform=trans, download=True)
        mnist_test = torchvision.datasets.FashionMNIST(
            root="./data", train=False, transform=trans, download=True)
        return (data.DataLoader(mnist_train, batch_size, shuffle=True,
                                num_workers=get_dataloader_workers()),
                data.DataLoader(mnist_test, batch_size, shuffle=False,
                                num_workers=get_dataloader_workers()))
    

1.5 ソフトマックス回帰の簡潔な実装

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt

# 设置随机种子以确保结果可重复
torch.manual_seed(42)

# 定义超参数
batch_size = 128        # 每个批次的样本数
learning_rate = 0.1     # 学习率,用于控制优化过程中参数更新的步长
num_epochs = 100        # 训练的轮数

# 加载 Fashion-MNIST 数据集
transform = transforms.Compose([
    transforms.ToTensor(),                # 将图像转换为张量
    transforms.Normalize((0.5,), (0.5,))  # 将像素值归一化到 [-1,1] 区间
])

# 加载训练集和测试集,并将数据转换为张量
train_dataset = torchvision.datasets.FashionMNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.FashionMNIST(root='./data', train=False, download=True, transform=transform)

# 创建训练集和测试集的数据加载器,用于批量获取数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

# 定义模型
# 创建了一个名为 SoftmaxRegression 的类,继承自 nn.Module
class SoftmaxRegression(nn.Module):
    def __init__(self, input_size, num_classes):  # 构造函数 init 初始化
        super(SoftmaxRegression, self).__init__()
        # 定义了一个线性层 (nn.Linear) 作为模型的唯一层次结构
        # 输入大小为 input_size,输出大小为 num_classes
        self.linear = nn.Linear(input_size, num_classes)

    # 实现了前向传播操作,将输入数据通过线性层得到输出
    def forward(self, x):
        out = self.linear(x)
        return out

model = SoftmaxRegression(input_size=784, num_classes=10)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()    # 用于计算多分类问题中的交叉熵损失
optimizer = optim.SGD(model.parameters(), lr=learning_rate)  # 定义随机梯度下降优化器,用于更新模型的参数

# 训练模型
train_losses = []
test_losses = []
# 在模型训练的过程中,运行模型对全部数据完成一次前向传播和反向传播的完整过程叫做一个 epoch
# 在梯度下降的模型训练的过程中,神经网络逐渐从不拟合状态到优化拟合状态,达到最优状态之后会进入过拟合状态
# 因此 epoch 并非越大越好。数据越多样,相应 epoch 就越大
for epoch in range(num_epochs):
    train_loss = 0.0

    # 1.将模型设置为训练模式
    model.train()  
    for images, labels in train_loader:
        # 将输入数据展平
        images = images.reshape(-1, 784)

        # 前向传播、计算损失、反向传播和优化
        outputs = model(images)
        loss = criterion(outputs, labels)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        train_loss += loss.item()

    # 2.将模型设置为评估模式(在测试集上计算损失)
    model.eval()  
    test_loss = 0.0
    correct = 0
    total = 0

    with torch.no_grad():
        for images, labels in test_loader:
            images = images.reshape(-1, 784)
            outputs = model(images)
            loss = criterion(outputs, labels)
            test_loss += loss.item()

            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    train_loss /= len(train_loader)
    test_loss /= len(test_loader)
    accuracy = 100 * correct / total

    train_losses.append(train_loss)
    test_losses.append(test_loss)

    print(f'Epoch [{
      
      epoch + 1}/{
      
      num_epochs}], Train Loss: {
      
      train_loss:.4f}, Test Loss: {
      
      test_loss:.4f}, Accuracy: {
      
      accuracy:.2f}%')

# 可视化损失
plt.plot(train_losses, label='Train Loss')
plt.plot(test_losses, label='Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()
# 输出
Epoch [1/100], Train Loss: 0.6287, Test Loss: 0.5182, Accuracy: 81.96%
Epoch [2/100], Train Loss: 0.4887, Test Loss: 0.4981, Accuracy: 82.25%
Epoch [3/100], Train Loss: 0.4701, Test Loss: 0.4818, Accuracy: 82.49%
Epoch [4/100], Train Loss: 0.4554, Test Loss: 0.4719, Accuracy: 82.90%
Epoch [5/100], Train Loss: 0.4481, Test Loss: 0.4925, Accuracy: 82.57%
Epoch [6/100], Train Loss: 0.4360, Test Loss: 0.4621, Accuracy: 83.53%
Epoch [7/100], Train Loss: 0.4316, Test Loss: 0.4662, Accuracy: 83.53%
Epoch [8/100], Train Loss: 0.4293, Test Loss: 0.4543, Accuracy: 83.80%
Epoch [9/100], Train Loss: 0.4289, Test Loss: 0.5460, Accuracy: 81.09%
...

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_42994487/article/details/132341723