Escriba un marco de aprendizaje profundo a mano (1) Cree una red neuronal con pytorch

objetivo pequeño

A partir de hoy, intentaré usar numpy para implementar un marco de trabajo de redes neuronales profundas y me referiré a la implementación de pytorch para lograr una comprensión más profunda de las redes neuronales.

objetivo.png

Referencias

  • George Hotz Compartir sobre tinygrad Compartir
  • documentación oficial de pytorch

referencia.jpeg

requerimientos básicos

  • Más información sobre el aprendizaje profundo en general
  • Familiarizado con el lenguaje de programación python
  • Competente en las principales bibliotecas de python como numpy y matplotlab
  • Familiarizarse con pytorch

requisito.jpeg

Preparación

Por el momento, vamos a construir un marco de aprendizaje profundo basado en numpy, numpyproporcionando buena información sobre operaciones de matriz y operaciones, lo que puede ahorrar mucho tiempo en la fabricación de ruedas demasiado básicas. El diseño de API del marco de aprendizaje profundo extraerá lecciones de torch, un marco de aprendizaje profundo modular y orientado a objetos, y usará torch como maestro para comparar e imitar paso a paso.

%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)

Preparar el conjunto de datos

Primero use pytorch para implementar una red neuronal simple, use la red neuronal para reconocer números escritos a mano, la entrada es un vector aplanado por una imagen digital y la salida es un valor numérico que representa el número.

Antes de comenzar, prepare un conjunto de datos adecuado. Esta vez, elegimos el conjunto de datos básico clásico: el conjunto de datos MNIST, que tiene 60 000 muestras de entrenamiento y 10 000 muestras de prueba. Después de tener el conjunto de datos, pytorch primero define una red neuronal de 2 capas y la entrena para que la red pueda reconocer el conjunto de datos MNIST. Luego intente implementar una red neuronal basada en numpy que implemente una clase, incluida la propagación hacia adelante y la propagación hacia atrás.

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)

Se puede ver que hay un total de 60k imágenes en el conjunto de datos de entrenamiento, cada tamaño de imagen es de 28 x 28, verifique el efecto de una imagen de muestra de datos más baja

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

001.png

Definir el modelo

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

red_neural.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)

训练

entrenamiento.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 ( X i ) j = 1 norte Exp ( X j ) \frac{\exp(x_i)}{\sum_{j=1}^n \exp(x_j)}

损失函数采用多分类 nn.CrossEntropyLoss() ,其实 CrossEntropyLoss 包括将输出进行 softmax 将输出标准化为概率,然后在用负对数似然来计算两个概率之间距离, y i Iniciar sesión ( 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}")

Antes de comenzar a calcular el gradiente, debe borrar el gradiente calculado anteriormente; de optim.zero_grad()​​lo contrario gradiente se acumulará. Luego ingrese la etiqueta verdadera Y y la salida prevista en la función de pérdida para calcular el valor de pérdida, y luego llame al loss.backward()método para realizar la propagación hacia atrás, que calculará la derivada de cada parámetro del modelo, y luego usará el gradiente para actualizar cada parámetro una vez , Este es optim.step()el trabajo a hacer.

plot(losses)

002.png

Estoy participando en el reclutamiento del programa de firma de creadores de la Comunidad Tecnológica de Nuggets, haga clic en el enlace para registrarse y enviar .

Supongo que te gusta

Origin juejin.im/post/7116868104113618957
Recomendado
Clasificación