手作業でディープラーニングフレームワークを作成する(1)pytorchを使用してニューラルネットワークを構築する

小さなターゲット

今日から、numpyを使用してディープニューラルネットワークフレームワークを実装し、pytorchの実装を参照してニューラルネットワークをより深く理解するようにします。

Objective.png

参考文献

  • ジョージホッツシェアtinygradシェアについて
  • pytorchの公式ドキュメント

reference.jpeg

基本的な要件

  • ディープラーニング全般について学ぶ
  • Pythonプログラミング言語に精通している
  • numpyやmatplotlabなどの主流のPythonライブラリに習熟している
  • pytorchに慣れる

require.jpeg

準備

当面は、numpyをベースにしたディープラーニングのフレームワークを構築し、numpy行列の操作と操作に関する優れた情報を提供します。これにより、基本的なホイールを作成する時間を大幅に節約できます。ディープラーニングフレームワークのAPI設計は、オブジェクト指向のモジュール式ディープラーニングフレームワークであるトーチから教訓を引き出し、トーチを教師として使用して段階的に比較および模倣します。

%pylab inline
import numpy as np
from tqdm import trange
np.set_printoptions(suppress=True)
import torch
import torch.nn as nn
# torch.set_printoptions(precision=2)
torch.set_printoptions(sci_mode=False)

データセットを準備する

まず、pytorchを使用して単純なニューラルネットワークを実装し、ニューラルネットワークを使用して手書きの数値を認識します。入力はデジタル画像によって平坦化されたベクトルであり、出力は数値を表す数値です。

開始する前に、適切なデータセットを準備します。今回は、従来のエントリレベルのデータセットであるMNISTデータセットを選択します。このデータセットには、60kのトレーニングサンプルと10kのテストサンプルが含まれています。データセットを取得した後、pytorchは最初に2層ニューラルネットワークを定義し、ネットワークがMNISTデータセットを認識できるようにトレーニングします。次に、順伝播と逆伝播を含む、クラスを実装するnumpyベースのニューラルネットワークの実装を試みます。

def fetch(url):
  import requests, gzip, os, hashlib, numpy
  fp = os.path.join("/tmp", hashlib.md5(url.encode('utf-8')).hexdigest())
  if os.path.isfile(fp):
    with open(fp, "rb") as f:
      dat = f.read()
  else:
    with open(fp, "wb") as f:
      dat = requests.get(url).content
      f.write(dat)
  return numpy.frombuffer(gzip.decompress(dat), dtype=np.uint8).copy()
X_train = fetch("http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz")[0x10:].reshape((-1, 28, 28))
Y_train = fetch("http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz")[8:]
X_test = fetch("http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz")[0x10:].reshape((-1, 28, 28))
Y_test = fetch("http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz")[8:]

minist_dataset.png

X_train.shape #(60000, 28, 28)

トレーニングデータセットには合計60kの画像があり、各画像サイズは28x28であることがわかります。より低いデータサンプル画像の効果を確認してください。

imshow(X_train[0],cmap='gray')

001.png

モデルを定義する

写一个简单 2 层的神经网络,通常输入层是不会计入神经网络的层数中。输入向量维度 784 向量是将图像 28 x 28 展平为 784 输入,输入样本形状(m,784) ,这里 m 表示一批次样本的数量。第一层神经网络参数(784x128) 经过第一层后形状(m,128)。这里激活函数选择 ReLU 这个激活函数。第二层神经网络参数(128,10) 输出形状(m,10) 10 对应于 10 个类别。

neuro_network.png

class ANet(torch.nn.Module):
    def __init__(self):
        super(ANet,self).__init__()
        self.l1 = nn.Linear(784,128)
        self.act = nn.ReLU()
        self.l2 = nn.Linear(128,10)
    def forward(self,x):
        x = self.l1(x)
        x = self.act(x)
        x = self.l2(x)
        return x
model = ANet()

需要输入数据格式和类型需要满足模型要求

  • 最后两个维度进行展平输入维度(m,784)
  • 类型为 pytorch 提供的 tensor 类型,数值类型为浮点类型的数据
model(torch.tensor(X_train[0:10].reshape((-1,28*28))).float())

epochs = 10
tbar = trange(epochs)
for i in tbar:
    tbar.set_description(f"iterate {i}\n")

每次迭代随机从数据集中抽取一定数量的样本,这里使用 np.random,.randint随机在指定区间内生成 size 个整数。

epochs = 10
batch_size = 32
tbar = trange(epochs)
for i in tbar:
    samp = np.random.randint(0,X_train.shape[0],size=(batch_size))
    print(samp)

训练

training.jpeg

开始训练,我们需要定义 epochs 也就是迭代次数,而不是 epoch,名字起的有点容易产生歧义,epoch 是将数据集所有数据都参与到训练一次,每次迭代样本数量用 batch_size 来定义也就是定义.

epochs = 10
batch_size = 32
tbar = trange(epochs)
# 定义损失函数,损失函数使用交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()
# 定义优化器
optim = torch.optim.Adam(model.parameters())
for i in (t:=trange(epochs)):
    #对数据集中每次随机抽取批量数据用于训练
    samp = np.random.randint(0,X_train.shape[0],size=(batch_size))
    X = torch.tensor(X_train[samp].reshape((-1,28*28))).float()
    Y = torch.tensor(Y_train[samp]).long()
    # 将梯度初始化
    optim.zero_grad()
    
    # 模型输出
    out = model(X)
    #计算损失值
    loss = loss_fn(out,Y)
    # 计算梯度
    loss.backward()
    # 更新梯度
    optim.step()
    t.set_description(f"loss {loss.item():0.2f}")

定义损失函数

数据经过神经网络输出为 10 维数据,这里 10 就是分类数量,也可以 C 来表示分类数量,那么就是 C 维,也就是输出为 (m,C) 数据,m 表示样本数量,C 表示每一个样本会对每一个类别输出一个值,表示属于某一个类别可能性。所以需要对这些数字进行一个标准化,也就是让这些输出为一个概率分布,概率值大表示属于某一个类别可能性大。通常用 softmax

exp (( バツ )。 j = 1 n exp (( バツ j )。 \frac{\exp(x_i)}{\sum_{j=1}^n \exp(x_j)}

损失函数采用多分类 nn.CrossEntropyLoss() ,其实 CrossEntropyLoss 包括将输出进行 softmax 将输出标准化为概率,然后在用负对数似然来计算两个概率之间距离, Y ログ (( s i ) -y_i \log(s_i) 这是 y i y_i 正确标签

l = ( x , y ) = L { l 1 , , l N } T l=(x,y) = L\{l_1,\cdots,l_N\}^T
l n = w y n exp ( x n , y n ) c = 1 C exp ( x n , c ) l_n = -w_{y_n} \frac{\exp(x_{n,y_n})}{\sum_{c=1}^C \exp(x_{n,c})}
epochs = 10
batch_size = 32
tbar = trange(epochs)
# 定义损失函数,损失函数使用交叉熵损失函数
loss_fn = nn.CrossEntropyLoss()
# 定义优化器
optim = torch.optim.Adam(model.parameters())

losses,accs = [],[]

for i in (t:=trange(epochs)):
    #对数据集中每次随机抽取批量数据用于训练
    samp = np.random.randint(0,X_train.shape[0],size=(batch_size))
    X = torch.tensor(X_train[samp].reshape((-1,28*28))).float()
    Y = torch.tensor(Y_train[samp]).long()
    # 将梯度初始化
    optim.zero_grad()
    
    # 模型输出
    out = model(X)
    #计算准确度
    pred = torch.argmax(out,dim=1)
    acc = (pred == Y).float().mean()
    
    #计算损失值
    loss = loss_fn(out,Y)
    # 计算梯度
    loss.backward()
    # 更新梯度
    optim.step()
    # 
    loss, acc = loss.item(),acc.item()
    losses.append(loss)
    accs.append(acc)
    t.set_description(f"loss:{loss:0.2f}, acc: {acc:0.2f}")

勾配の計算を開始する前に、以前に計算された勾配をクリアする必要があります。optim.zero_grad()そう勾配が累積されます。次に、真のラベルYと予測出力を損失関数に入力して損失値を計算し、loss.backward()メソッドバックプロパゲーションを実行します。これにより、各モデルパラメーターの導関数が計算され、勾配を使用して各パラメーターが1回更新されます。 、これはoptim.step()やるべき仕事です。

plot(losses)

002.png

ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。

おすすめ

転載: juejin.im/post/7116868104113618957