[ハンズオンディープラーニング] Li Mu - 多層パーセプトロン

多層パーセプトロンをゼロから実装

import torch
from matplotlib import pyplot as plt
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

# 初始化模型参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256
W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True))
W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]


def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)


def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X @ W1 + b1)  # @代表矩阵乘法的简写
    return H @ W2 + b2


loss = nn.CrossEntropyLoss(reduction='none')

num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)
plt.show()
d2l.predict_ch3(net,test_iter)
plt.show()

多層パーセプトロンのシンプルな実装

import torch
from matplotlib import pyplot as plt
from torch import nn
from d2l import torch as d2l

net = nn.Sequential(nn.Flatten(), nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10))


def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01)


net.apply(init_weights)


batch_size, lr, num_epochs = 256, 0.01, 10
loss = nn.CrossEntropyLoss(reduction='none')


train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)

optimer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter,test_iter, loss, num_epochs, optimer)


plt.show()

体重減少

シンプルな実装

net = nn.Sequential(nn.Linear(num_inputs, 1))
for param in net.parameters():
	param.data.normal_()
optimer = torch.optim.SGD([ {"params":net[0].weight,'weight_decay':wd},
{"params":net[0].bias}],lr=lr)

上記で「weight_decay」を wd に設定することは、重み減衰を使用するように設定することです。

ドロップアウト

優れたモデルは、入力データの外乱に対して堅牢である必要があります。つまり、ノイズの影響を受けてはなりません。したがって、ノイズを含むデータを学習に使用する場合、ノイズの部分を学習しないようにできれば、それは正則化と同等になります。そこで、レイヤー間にノイズを加えるのがドロップアウト方式です。

定義方向から開始すると、バイアスなしでノイズを追加する、つまり元の入力に x \pmb{x} バツx加入噪音得到 x ′ \pmb{x}^{\prime} バツバツ' では、平均値が変わらないことを望みます。つまり、
E [ x ' ] = x E[\pmb {x}^{\ プライム}]=\pmb{x} E[バツバツ']=バツx
次に、メソッドを破棄する具体的な方法は、各要素に対して次の摂動を実行するです。 /span> a>
x i ' = { 0 w i th p r o b a b l i t y p x i 1 − p o th e r w i s e x^{\prime}_i=\begin{cases} 0\quad with~probability~p\\ \frac{x_i }{1-p} \quad それ以外の場合は \end{cases} バツ' ={ 0withp バブlit yp  1pバツi えー
これにより、期待値が変更されないことが保証されます。
E [ x i ' ] = p × 0 + ( 1 − p ) × x i 1 − p = x i E[x^ {\ prime}_i]=p\times 0 + (1-p)\times \frac{x_i}{1-p}=x_i E[x' ]=p×0+(1p)×1pバツi =バツi
この ドロップ確率は、モデルの複雑さを制御するハイパーパラメータです

具体的には、通常、ドロップアウト メソッドは多層パーセプトロンの隠れ層の出力に適用されます。つまり、あ>

ここに画像の説明を挿入します

これはトレーニング プロセス中に使用され、モデル パラメーターの更新に影響します。決定的な出力を保証するために、テスト中に dorpout 操作は実行されません。 。実験的には、正則化と同様の効果を達成できます。

すると、隠れ層の Dropout の出力は、今回 0 に設定されたニューロンの重みを更新しないので、各 Dropout が隠れ層のすべてのニューロンから一部を選択すると考えることができ、更新を行います。

特定の実装では、nn.Dropout() レイヤーを直接呼び出すことができます。

数値安定性

勾配を計算する場合:

ここに画像の説明を挿入します

ベクトルの導出は行列であるため、多くの行列演算では勾配爆発や勾配消失の問題が発生する可能性があります。

行列内のほとんどの勾配が 1 よりわずかに大きい数値であると仮定すると、非常に多くの勾配計算の後、勾配が大きすぎて爆発する可能性があります。勾配が 1 よりわずかに小さい場合、その後は 0 に近くなります。何度も繰り返します。

ここに画像の説明を挿入します

その場合、勾配爆発は次の問題を引き起こします。

  • 値が数値型で表現できる範囲を超えています
  • 学習率に対する感度がより高い
    • 学習率が比較的大きい場合、大きな勾配を掛けると更新の度合いが比較的大きくなり、安定させることが難しくなります。
    • 学習率が小さすぎると、勾配爆発以外の通常の重みが正常に更新できなくなる可能性があります。

たとえば、勾配消失の場合は、シグモイド関数が使用されます。

ここに画像の説明を挿入します

このような小さな勾配は、複数の重ね合わせの後に勾配が消失するという問題を引き起こす可能性があります。その主な問題は次のとおりです。

  • また、表現範囲を超えてしまい、直接、ほとんどのグラデーション値が 0 になり、更新できなくなります。
  • 勾配値が0のためトレーニングが正常に更新できません
  • これは、出力層からの逆伝播によって勾配が計算されるため、最下層では特に深刻です。最下層に到達するにつれて、より多くの層が重ねられるほど、勾配が消える可能性が高くなります。つまり、最上層だけが重なり合うことになります。正常にトレーニングおよび更新できます。

トレーニングをより安定させる方法の主な目標は、勾配値を妥当な範囲内にすることです。たとえば、アルゴリズムは、勾配の乗算を加算に変換したり、勾配を正規化したり、クリップしたりします。ただし、重みを適切に初期化し、適切な活性化関数を選択するという別の重要な方法があります。

具体的には、結論は、重みを初期化するときに、重みを平均 0 と分散から開始する γ t = 2 n t − 1 + n t \gamma_t=\frac{2}{n_{t-1}+n_t} ct =nt1 +nt 2 からサンプリングされました。そのうち n t − 1 、n t n_{t-1}、n_{t} nt1 nt は、この重みによって接続された 2 つの層内のニューロンの数を表します。したがってレイヤーの形状に基づいて重みが従う分布の分散を選択する必要があります。

活性化関数の導出後、2 つの活性化関数 Tanh(x) と ReLU(x) はより良い特性を持つことができると考えられますが、sigmoid(x) は次のように調整する必要があります。< a i=1 > 4 × s i g m o i d ( x ) − 2 4\times sigmoid(x)-2 4×sigmo id(x)2 では、最初の 2 つと同じ効果が得られます。

環境と流通の変化

1. 物流シフトの種類

オフセットには主に次の種類があります。

  • 共変量シフト:データの分布 p ( x ) p(x) を指します。p(x)変更、たとえば、トレーニング中に使用されるトレーニング データセットの分布 p 1 ( x ) p_1(x) p1 (x) およびテスト中に使用されるテスト セットの配布p2 (x) が異なる場合、モデルをテストで実行するのは困難ですデータセットは良好です。ただし、この変更には別のフレームワーク設計があります。入力の分布は時間の経過とともに変化する可能性がありますが、ラベル関数 (つまり、条件付き分布 P ( y ∣ x ) P(y\mid x) P(yx)) は変更されません。たとえば、トレーニング中は本物の猫と犬を使用して機械に分類を学習させますが、テスト中は漫画の猫と犬を使用します。つまり、トレーニング データ セットとテスト データ セットは異なりますが、そのラベル関数は猫と犬を同じように正しくラベル付けします。
  • ラベル シフト: 共変量シフトの逆の問題を指します。ここではラベル エッジ確率 P ( y ) P ( y) P(y) は変更できますが、カテゴリ 条件付き分布 P ( x ∣ y ) P(x\mid y) P(xy)ドメイン間で一定のままです。ここでの例は、患者の病気を予測することです。症状は x で、病気のラベルは y です。その後、病気の相対的な有病率、またはさまざまな病気間の比率が変化する可能性があります (つまり、 P(y)) は変更される可能性があります。特定の病気に対応する症状 ( P ( x ∣ y ) ) P(x\mid y)) P(xy))不会发生变化。
  • コンセプトシフト:レーベルの定義の変更を指します。たとえば、私たちの美しさの定義は時間の経過とともに変化する可能性があり、「美しさ」というラベルの概念も変化します。
2. 分布オフセット補正

まず、経験的リスクと実際のリスクが何であるかを理解する必要があります。トレーニング中は通常、損失関数を (正則化項を考慮せずに) 最小化します。つまり、
min ⁡ f 1 N u m ∑ i = 1 N u m l o s s ( f ( x i ) , y i ) \min_{f} \frac{1}{Num}\sum_{i=1}^{Num} loss(f(x_i),y_i) < /span>f Num1 i=1Num loss(f( ×i )そしてi )
トレーニング データ セットでのこの項の損失は、経験リスクと呼ばれます。次に、経験的リスクは、実際のデータ分布における損失である実際のリスクを近似することになります。しかし、実際には実際のデータの分布を取得することはできません。したがって、経験的リスクの最小化は実際のリスクの最小化によって近似できると一般に考えられています。

共変量シフト補正

既存のデータセット (x,y) については、 P ( y ∣ x ) P(y\mid x) を評価する必要がありますP(yx),但是当前的数据 x i x_i バツi はソース ディストリビューションから派生しています q ( x ) q(x) q(x)(次のように考えることができます)ターゲット分布から導き出すのではなく、データセットのトレーニング分布) p ( x ) p(x) p(x)(true とみなすことができますデータの分布、またはテストデータの分布を考慮)。ただし、 p ( y ∣ x ) = q ( y ∣ x ) p(y\mid x)=q(y\mid x) という共変量シフトの仮定があります。あ>p(yx)=q(yx)。 したがって、
∬ l o s s ( f ( x ) , y ) p ( x ) d x d y = ∬ l o s s ( f ( x ) , y ) q ( y ∣ x ) q ( x ) p ( x ) q ( x ) d x d y \iint loss(f(x),y)p(x)dxdy~= ~ \iint loss(f(x),y)q(y\mid x)q(x)\frac{p(x)}{q(x)}dxdy loss(f( x),y)p(x )dxdy = loss(f( x),y)q(yx)q(x )q(x)p(x)dxdy
したがって、現時点では次のことが必要です。計算ターゲット分布とソース分布のデータ間の比率を使用して、各サンプルの重みを再計算します。つまり、
bi =q(xi )p(xi )
次に、この重みを各データ サンプルに代入すると、重み付き経験的リスク最小化を使用してモデルをトレーニングできます。
min ⁡ f 1 N u m ∑ i = 1 N u m β i l o s s ( f ( x i ) , y ) \min_f \frac{1}{Num}\sum_{i=1}^{Num}\beta_i loss(f(x_i),y) f Num1 i=1Num bi loss(f( ×i )y)
因此接下来的问题就是估计 β \beta β。具体的な方法は次のとおりです。分布を推定するために 2 つの分布からサンプルを抽出する。つまり、ターゲット分布の場合 p ( x ) p(x) p(x)テスト データにアクセスできます取得するように設定し、ソース ディストリビューションの場合は q ( x ) q(x) q(x) はトレーニング データを直接渡します取得を設定します。ここでは、テスト データ セットへのアクセスがデータ漏洩につながるかどうかを検討する必要があります。実際には、 機能にアクセスしただけであるため、データ漏洩につながることはありません。 x ∼ p ( x ) x \sim p(x) バツp(x) にアクセスしませんでした。 y とタグ付けします。この方法では、 β \beta β:对数几率回归。

同じデータのサンプルが 2 つの分布から抽出されたと仮定します。p のサンプル データ ラベルは z=1、q のサンプル データ ラベルは z=-1 です。したがって、この混合データセットの確率は次のようになります。
P ( z = 1 ∣ x ) = p ( x ) p ( x ) + q ( x ) P ( z = 1 ∣ x ) P ( z = − 1 ∣ x ) = p ( x ) q ( x ) P(z=1\mid x)=\frac{p(x)}{p(x)+q(x)}\\ \frac { P(z=1\mid x)}{P(z=-1\mid x)}=\frac{p(x)}{q(x)} P(z=1x)=p(x)+q(x)p(x)P(z=1x)P(z=1x) =q(x)p(x)
したがって、対数オッズ回帰法を使用すると、 P ( z = 1 ∣ x ) = 1 1 + e x p ( − h ( x ) ) P(z= 1\mid x)=\frac{1}{1+exp(-h(x))} P(z=1x)=1+exp (h(x ))1 (h はパラメータ化された関数、設定) の場合、次のようになります:
β i = P ( z = 1 ∣ x i ) P ( z = − 1 ∣ x i ) = e x p ( h ( x i ) ) \beta_i = \frac{P(z=1\mid x_i)}{P(z=-1 \mid x_i)}=exp(h(x_i)) bi =P(z=1バツi )P(z=1バツi ) =exp(h (xi ))
したがって、トレーニングが h ( x ) h(x) h(x)即可。

ただし、上記のアルゴリズムは重要な前提に依存しています。ターゲット分布 (テスト セット分布) 内の各データ サンプルがトレーニング中に出現する確率がゼロではないことが必要です。 < a i=2>、それ以外の場合は p ( x i ) > 0 , q ( x i ) = 0 p(x_i)>0,q(x_i)= と表示されます。 0p(xi )>0q(xi )=0的情况。

ラベルオフセット補正

同様に、ここではラベルの分布が時間とともに変化すると仮定します。 q ( y ) ≠ p ( y ) q(y)\neq p(y) < /span>q(y)=p(y) ただし、カテゴリは条件付きです分布は変更されません q ( x ∣ y ) = p ( x ∣ y ) q(x\mid y)=p(x\mid y) q(xy)=p(xy)。那么:
∬ l o s s ( f ( x ) , y ) p ( x ∣ y ) p ( y ) d x d y = ∬ l o s s ( f ( x ) , y ) q ( x ∣ y ) q ( y ) p ( y ) q ( y ) d x d y \iint loss(f(x),y)p (x\mid y)p(y)dxdy=\iint loss(f(x),y)q(x\mid y)q(y)\frac{p(y)}{q(y)}dxdy < /span>loss(f( x),y)p(xy)p(y )dxdy=loss(f( x),y)q(xy)q(y )q(y)p(y)dxdy
因此重要性权重将对应于标签似然比率:
β i = p ( y i ) q ( y i ) \beta_i=\frac{p(y_i)}{q(y_i)} bi =q(yi )p(yi )
なぜなら、ターゲット ラベルの分布を考慮するために、まず、かなり優れたパフォーマンスを持つ既製の分類器 (通常はトレーニング データに基づいてトレーニングされる) を使用し、検証セットを使用して混同行列。この場合、混同行列は k × k k\times k となります。k×k の行列 (k は分類カテゴリの数)。 各セルの値 c i j c_{ij} cij は、真のラベルが j でモデルが i を予測する検証セット内のサンプルの割合です。

しかし、実際の分布がわからないため、ターゲット データの混同行列を計算することはできません。次に、テスト中に既存のモデルの予測を平均して、平均モデル出力を取得することができます。 μ ( y ^ ) ∈ R k \mu (\hat{ y})\in R^k μ(そして^ )Rk。ここで、i 番目の要素は、テスト セット内の i 番目のカテゴリを予測するモデルの合計予測スコアです。

具体的には、分類器が最初から非常に正確で、ターゲット データに以前に見たカテゴリのみが含まれている (トレーニング セットとテスト セットが同じカテゴリを持っている) 場合、ラベル If オフセット仮定が成り立つ場合、テスト セットのラベル分布は単純な線形システムを通じて推定できます。
C p ( y ) = μ ( y ^ ) Cp(y)=\mu(\ hat{ y}) Cp(y)=μ(そして^ )
C を無限に解く:
p ( y ) = C − 1 µ ( y ^ ) p(y) =C^{ -1}\mu(\hat{y}) p(y)=C1μ(そして^ )

コンセプトズレ補正

これを正確な方法で修正することは困難です。ただし、そのような変化は通常はまれであるか、非常に遅いです。一般に私たちができることは、トレーニング中にネットワークの変化に適応し、新しいデータを使用してネットワークを更新することです。

実践的な kaggle コンテスト: 住宅価格の予測

import numpy as np
import pandas as pd
import torch
from matplotlib import pyplot as plt
from torch import nn
from d2l import torch as d2l
import hashlib
import os
import tarfile
import zipfile
import requests

DATA_HUB = dict()
DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/'


def download(name, cache_dir=os.path.join("dataset", "data_kaggle")):  # @save
    assert name in DATA_HUB, f"{
      
      name} 不存在于 {
      
      DATA_HUB}"
    url, shal_hash = DATA_HUB[name]
    os.makedirs(cache_dir, exist_ok=True)  # 按照第一个参数创建目录,第二参数代表如果目录已存在就不发出异常
    fname = os.path.join(cache_dir, url.split('/')[-1])
    if os.path.exists(fname):  # 如果已存在这个数据集
        shal = hashlib.sha1()
        with open(fname, 'rb') as f:
            while True:
                data = f.read(1048576)  # 这里进行数据集的读取,一次能够读取的最大行数为1048576
                if not data:  # 如果读取到某一次不成功
                    break
                shal.update(data)
        if shal.hexdigest() == shal_hash:
            return fname  # 命中缓存
    print(f'正在从{
      
      url}下载{
      
      fname}...')
    r = requests.get(url, stream=True, verify=True)
    # 向链接发送请求,第二个参数是不立即下载,当数据迭代器访问的时候再去下载那部分,不然全部载入会爆内存,第三个参数为不验证证书
    with open(fname, 'wb') as f:
        f.write(r.content)
    return fname


# 下载并解压一个zip或tar文件
def download_extract(name, folder=None):  # @save
    fname = download(name)
    base_dir = os.path.dirname(fname)  # 获取文件的路径,fname是一个相对路径,那么就返回从当前文件到目标文件的路径
    data_dir, ext = os.path.splitext(fname)  # 将这个路径最后的文件名分割,返回路径+文件名,和一个文件的扩展名
    if ext == '.zip':  # 如果为zip文件
        fp = zipfile.ZipFile(fname, 'r')
    elif ext in ('.tar', '.gz'):
        fp = tarfile.open(fname, 'r')
    else:
        assert False, "只有zip/tar文件才可以被解压缩"
    fp.extractall(base_dir)  # 解压压缩包内的所有文件到base_dir
    return os.path.join(base_dir, folder) if folder else data_dir


def download_all():  # @save
    for name in DATA_HUB:
        download(name)


# 下载并缓存房屋数据集
DATA_HUB['kaggle_house_train'] = (  # @save
    DATA_URL + 'kaggle_house_pred_train.csv',
    '585e9cc93e70b39160e7921475f9bcd7d31219ce'
)

DATA_HUB['kaggle_house_test'] = (  # @save
    DATA_URL + 'kaggle_house_pred_test.csv',
    'fa19780a7b011d9b009e8bff8e99922a8ee2eb90'
)

train_data = pd.read_csv(download('kaggle_house_train'))
test_data = pd.read_csv(download('kaggle_house_test'))

# print(train_data.shape)
# print(test_data.shape)
# print(train_data.iloc[0:4,[0,1,2,3,-3,-2,-1]])

# 将序号列去掉,训练数据也不包含最后一列的价格列,然后将训练数据集和测试数据集纵向连接在一起
all_features = pd.concat((train_data.iloc[:, 1:-1], test_data.iloc[:, 1:]))

# 将数值型的数据统一减去均值和方差
numeric_features = all_features.dtypes[all_features.dtypes != 'object'].index  # 在panda中object类型代表字符串
all_features[numeric_features] = all_features[numeric_features].apply(
    lambda x: (x - x.mean()) / (x.std())  # 应用匿名函数
)
# 在标准化数据后,所有均值消失,因此我们可以设置缺失值为0
all_features[numeric_features] = all_features[numeric_features].fillna(0)

# 对离散值进行处理
all_features = pd.get_dummies(all_features, dummy_na=True)  # 第二个参数代表是否对nan类型进行编码

# print(all_features.shape)

n_train = train_data.shape[0]  # 训练数据集的个数
train_features = torch.tensor(all_features[:n_train].values, dtype=torch.float32)  # 取出训练数据
test_features = torch.tensor(all_features[n_train:].values, dtype=torch.float32)  # 取出测试数据
train_labels = torch.tensor(train_data.SalePrice.values.reshape(-1, 1), dtype=torch.float32)  # 取出训练数据的价格列

loss = nn.MSELoss()
in_features = train_features.shape[1]  # 特征的个数


# 网络架构
def get_net():
    net = nn.Sequential(nn.Linear(in_features, 1))
    return net


# 取对数约束输出的数量级
def log_rmes(net, features, labels):
    clipped_preds = torch.clamp(net(features), 1, float('inf'))
    # 第一个为要约束的参数,第二个为最小值,第三个为最大值,小于最小值就为1
    rmse = torch.sqrt(loss(torch.log(clipped_preds), torch.log(labels)))
    return rmse.item()


# 训练的函数
def train(net, train_features, train_labels, test_features, test_labels, num_epochs, learning_rate,
          weight_decay, batch_size):
    train_ls, test_ls = [], []
    train_iter = d2l.load_array((train_features, train_labels), batch_size)  # 获取数据迭代器
    optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate, weight_decay=weight_decay)
    # 这是另外一个优化器,它对lr的数值不太敏感,第三个参数代表是否使用正则化
    for epoch in range(num_epochs):
        for X, y in train_iter:
            optimizer.zero_grad()  # 梯度先清零
            l = loss(net(X), y)  # 计算损失
            l.backward()  # 反向传播计算梯度
            optimizer.step()  # 更新参数
        train_ls.append(log_rmes(net, train_features, train_labels))
        if test_labels is not None:
            test_ls.append(log_rmes(net, test_features, test_labels))
    return train_ls, test_ls


# K折交叉验证
def get_k_fold_data(k, i, X, y):
    assert k > 1
    fold_size = X.shape[0] // k
    X_train, y_train = None, None
    for j in range(k):
        idx = slice(j * fold_size, (j + 1) * fold_size)  # 创建一个切片对象
        X_part, y_part = X[idx, :], y[idx]  # 将切片对象应用于索引
        if j == i:  # 取出第i份作为验证集
            X_valid, y_valid = X_part, y_part
        elif X_train is None:  # 如果当前训练集没有数据就初始化
            X_train, y_train = X_part, y_part
        else:
            X_train = torch.cat([X_train, X_part], 0)  # 如果是训练集那么就进行合并
            y_train = torch.cat([y_train, y_part], 0)
    return X_train, y_train, X_valid, y_valid


# k次的k折交叉验证
def k_fold(k, X_train, y_train, num_epochs, learning_rate, weight_decay, batch_size):
    train_l_sum, valid_l_sum = 0, 0
    for i in range(k):
        data = get_k_fold_data(k, i, X_train, y_train)
        net = get_net()
        train_ls, valid_ls = train(net, *data, num_epochs, learning_rate, weight_decay, batch_size)
        train_l_sum += train_ls[-1]
        valid_l_sum += valid_ls[-1]
        if i == 0:
            d2l.plot(list(range(1, num_epochs + 1)), [train_ls, valid_ls], xlabel="epoch",
                     ylabel='ylabel', xlim=[1, num_epochs], legend=["train", 'valid'], yscale='log')
        print(f"折{
      
      i + 1},训练log rmse{
      
      float(train_ls[-1]):f},"
              f"验证log rmse{
      
      float(valid_ls[-1]):f}")
    return train_l_sum / k, valid_l_sum / k


k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64
train_l,valid_l = k_fold(k,train_features, train_labels, num_epochs, lr, weight_decay, batch_size)
print(f"{
      
      k}折验证:平均训练log rmse:{
      
      float(train_l):f}",
      f"平均验证log rmse:{
      
      float(valid_l):f}")
plt.show()

以下は私自身のデバッグ結果です。

def get_net():
    net = nn.Sequential(nn.Linear(in_features, 256),
                        nn.ReLU(),
                        nn.Linear(256,1))
    return net
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 5, 0, 64
5折验证:平均训练log rmse:0.045112 平均验证log rmse:0.157140

私は常に 256 から 1 に直接移行するのは良くないと感じていたので、モデルの構造を調整しました。

def get_net():
    net = nn.Sequential(nn.Linear(in_features, 128),
                        nn.ReLU(),
                        nn.Linear(128,1))
    return net
k, num_epochs, lr, weight_decay, batch_size = 5, 100, 0.03, 1, 64
5折验证:平均训练log rmse:0.109637 平均验证log rmse:0.136201

より複雑なモデルでは、誤差を減らす方法がないと常に感じられます。

おすすめ

転載: blog.csdn.net/StarandTiAmo/article/details/127340944