小さなターゲット
今日から、numpyを使用してディープニューラルネットワークフレームワークを実装し、pytorchの実装を参照してニューラルネットワークをより深く理解するようにします。
参考文献
- ジョージホッツシェアtinygradシェアについて
- pytorchの公式ドキュメント
基本的な要件
- ディープラーニング全般について学ぶ
- Pythonプログラミング言語に精通している
- numpyやmatplotlabなどの主流のPythonライブラリに習熟している
- pytorchに慣れる
準備
当面は、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:]
X_train.shape #(60000, 28, 28)
トレーニングデータセットには合計60kの画像があり、各画像サイズは28x28であることがわかります。より低いデータサンプル画像の効果を確認してください。
imshow(X_train[0],cmap='gray')
モデルを定義する
写一个简单 2 层的神经网络,通常输入层是不会计入神经网络的层数中。输入向量维度 784 向量是将图像 28 x 28 展平为 784 输入,输入样本形状(m,784) ,这里 m 表示一批次样本的数量。第一层神经网络参数(784x128) 经过第一层后形状(m,128)。这里激活函数选择 ReLU 这个激活函数。第二层神经网络参数(128,10) 输出形状(m,10) 10 对应于 10 个类别。
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)
训练
开始训练,我们需要定义 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
损失函数采用多分类 nn.CrossEntropyLoss()
,其实 CrossEntropyLoss
包括将输出进行 softmax
将输出标准化为概率,然后在用负对数似然来计算两个概率之间距离,
这是
正确标签
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)
ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。