写点什么

pytorch 实现前馈神经网络实验(手动实现)

作者:Studying_swz
  • 2023-06-10
    天津
  • 本文字数:9540 字

    阅读完需:约 31 分钟

pytorch实现前馈神经网络实验(手动实现)

一:“手动实现前馈神经网络解决回归、二分类、多分类任务”实验


1.1“手动实现前馈神经网络解决回归”实验实验过程:1.1.1 导入所需要的包


1.  import torch  2.  import numpy as np  3.  import random  4.  from IPython import display  5.  from matplotlib import pyplot as plt  6.  import torch.utils.data as Data  
复制代码


1.1.2 自定义数据


1.  #自定义数据---训练集  2.  num_inputs = 500  3.  num_examples = 10000  4.  true_w = torch.ones(500,1)*0.0056  5.  true_b = 0.028  6.  x_features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)  7.  y_labels = torch.mm(x_features,true_w) + true_b  8.  y_labels += torch.tensor(np.random.normal(0, 0.01, size=y_labels.size()), dtype=torch.float)  9.  #训练集  10.  trainfeatures =x_features[:7000]  11.  trainlabels = y_labels[:7000]  12.  print(trainfeatures.shape)  13.  #测试集  14.  testfeatures =x_features[7000:]  15.  testlabels = y_labels[7000:]  16.  print(testfeatures.shape) 
复制代码


注:这里按照 ppt 的实验要求,进行构建线性数据集,样本的特征和真值其主要是满足高维线性关系,对于样本的特征来说,我这里定义的是服从均值为 0,标准差为 1 的分布,所以相比于分散的数据,其是比较复杂的,这里样本的数量是 10000,其中划分为训练集为 7000,测试集为 3000。1.1.3 读取数据


1.  #读取数据  2.  batch_size = 50  3.  # 将训练数据的特征和标签组合  4.  dataset = Data.TensorDataset(trainfeatures, trainlabels)  5.  # 把 dataset 放入 DataLoader  6.  train_iter = Data.DataLoader(  7.      dataset=dataset, # torch TensorDataset format  8.      batch_size=batch_size, # mini batch size  9.      shuffle=True, # 是否打乱数据 (训练集一般需要进行打乱)  10.      num_workers=0, # 多线程来读数据, 注意在Windows下需要设置为0  11.  )  12.  # 将测试数据的特征和标签组合  13.  dataset = Data.TensorDataset(testfeatures, testlabels)  14.  # 把 dataset 放入 DataLoader  15.  test_iter = Data.DataLoader(  16.      dataset=dataset, # torch TensorDataset format  17.      batch_size=batch_size, # mini batch size  18.      shuffle=True, # 是否打乱数据 (训练集一般需要进行打乱)  19.      num_workers=0, # 多线程来读数据, 注意在Windows下需要设置为0  20.  )
复制代码


注:利用 Data.DataLoader 读取数据,并设置进行批量读取的大小,后面的实验在读取数据部分都是利用的 torch 自带的这个模块进行读取,之后不再加以赘述。1.1.4 初始化参数


1.  #初始化参数  2.  num_hiddens,num_outputs = 256,1  3.    4.  W1 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens,num_inputs)), dtype=torch.float32)  5.  b1 = torch.zeros(1, dtype=torch.float32)  6.  W2 = torch.tensor(np.random.normal(0, 0.01, (num_outputs,num_hiddens)), dtype=torch.float32)  7.  b2 = torch.zeros(1, dtype=torch.float32)  8.  params =[W1,b1,W2,b2]  9.  for param in params:  10.      param.requires_grad_(requires_grad=True)  
复制代码


注:可由模型设计部分知道其输入层、隐藏层、输出层分别是 500,256,1。这里初始化参数则需要分别初始化输入层和隐藏层之间的 W1 和 b1,以及隐藏层和输出层之间的 W2 和 b2,这里设置的 W 的初始是服从均值为 0,方差为 0.01 的高斯分布,b 的初值是 0;然后需要设置梯度更新为 true。1.1.5 定义隐藏层的激活函数


1.  def relu(x):  2.      x = torch.max(input=x,other=torch.tensor(0.0))  3.      return x 
复制代码


注:这里的激活函数我设计的是 ReLU 激活函数,因为其 x>0 部分的不存在梯度消失等问题,对于隐藏的单元的激活函数普遍使用的是 ReLU 激活函数。1.1.6 定义模型


1.  #定义模型  2.  def net(X):  3.      X = X.view((-1,num_inputs))  4.      H = relu(torch.matmul(X,W1.t())+b1)  5.      return torch.matmul(H,W2.t())+b2  
复制代码


注:这里是手动实现模型的关键,首先 H 得到的是隐藏层经过激活函数后的输出,最后返回全连接得到输出层结果。1.1.7 定义最小化误差以及随机梯度下降法


1.  #定义最小化均方误差  2.  loss = torch.nn.MSELoss()  3.    4.  #定义随机梯度下降法  5.  def SGD(paras,lr,batch_size):  6.      for param in params:  7.          param.data -= lr * param.grad/batch_size  
复制代码


注:因为是回归问题,这里使用的是 torch 模块自带的最小化均方误差,然后梯度下降法使用的是小批量随机梯度下降法。


1.1.8 定义训练函数


1.  #定义模型训练函数  2.  def train(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,optimizer=None):  3.      train_ls = []  4.      test_ls = []  5.      for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期  6.          train_l_sum, train_acc_num,n = 0.0,0.0,0  7.          # 在每一个迭代周期中,会使用训练数据集中所有样本一次  8.          for X, y in train_iter: # x和y分别是小批量样本的特征和标签  9.              y_hat = net(X)  10.              l = loss(y_hat, y.view(-1,1)) # l是有关小批量X和y的损失  11.              #梯度清零  12.              if optimizer is not None:  13.                  optimizer.zero_grad()  14.              elif params is not None and params[0].grad is not None:  15.                  for param in params:  16.                      param.grad.data.zero_()  17.              l.backward() # 小批量的损失对模型参数求梯度  18.              if optimizer is None:  19.                  SGD(params,lr,batch_size)  20.              else:  21.                  optimizer.step()  22.          train_labels = trainlabels.view(-1,1)  23.          test_labels = testlabels.view(-1,1)  24.          train_ls.append(loss(net(trainfeatures),train_labels).item())  25.          test_ls.append(loss(net(testfeatures),test_labels).item())  26.          print('epoch %d, train_loss %.6f,test_loss %f'%(epoch+1, train_ls[epoch],test_ls[epoch]))  27.      return train_ls,test_ls  
复制代码


注:这个训练模型主要借鉴的 ppt 上的训练,在参数设置中,它根据传入的参数值,来区分是手动实现,还是 torch 模块实现,然后在每次 epoch 后,重新利用 loss 计算训练集和测试集的 loss,保存到列表中,然后返回。1.1.9 开始训练模型


1.  lr = 0.05  2.  num_epochs = 500  3.  train_loss,test_loss = train(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr)  
复制代码


注:调用之前的模型函数,传入参数:学习率,迭代次数,批量大小等,进行训练。1.1.10 绘制训练集和测试集的 loss 曲线


1.  x = np.linspace(0,len(train_loss),len(train_loss))  2.  plt.plot(x,train_loss,label="train_loss",linewidth=1.5)  3.  plt.plot(x,test_loss,label="test_loss",linewidth=1.5)  4.  plt.xlabel("epoch")  5.  plt.ylabel("loss")  6.  plt.legend()  7.  plt.show()  
复制代码


注:这里也是借鉴 ppt 的绘图部分,主要是利用 matplotlib 库,进行绘制,横坐标是迭代次数,纵坐标分别之前保存的训练集误差和测试集误差。


1.1.2“手动实现前馈神经网络解决二分类”实验实验过程:1.2.1 导入所需要的包


1.  import torch  2.  import numpy as np  3.  import random  4.  from IPython import display  5.  from matplotlib import pyplot as plt  6.  import torch.utils.data as Data 
复制代码


1.2.2 自定义数据


1.  #自定义数据---训练集  2.  num_inputs = 200  3.  #1类  4.  x1 = torch.normal(2,1,(10000, num_inputs))  5.  y1 = torch.ones(10000,1) # 标签1   6.  x1_train = x1[:7000]  7.  x1_test = x1[7000:]  8.  #0类  9.  x2 = torch.normal(-2,1,(10000, num_inputs))  10.  y2 = torch.zeros(10000,1) # 标签0  11.  x2_train = x2[:7000]  12.  x2_test = x2[7000:]  13.  #合并训练集  14.  # 注意 x, y 数据的数据形式一定要像下面一样 (torch.cat 是合并数据)---按行合并  15.  trainfeatures = torch.cat((x1_train,x2_train), 0).type(torch.FloatTensor)  16.  trainlabels = torch.cat((y1[:7000], y2[:7000]), 0).type(torch.FloatTensor)  17.  #合并测试集  18.  # 注意 x, y 数据的数据形式一定要像下面一样 (torch.cat 是合并数据)---按行合并  19.  testfeatures = torch.cat((x1_test,x2_test), 0).type(torch.FloatTensor)  20.  testlabels = torch.cat((y1[7000:], y2[7000:]), 0).type(torch.FloatTensor)  21.  print(trainfeatures.shape,trainlabels.shape,testfeatures.shape,testlabels.shape)
复制代码


注:按实验要求,这里生成了两个数据集,第一类为均值为 2,标准差为 1,其标签为 1,训练集和测试集个数分别是 7000,3000;第二类为均值为-2,标准差为 1,其标签为 0,训练集和测试集个数分别是 7000,3000。然后利用 torch.cat()操作将两类的训练集和测试集分别合并,从而训练集的大小为 14000,测试集的大小为 6000。1.2.3 读取数据


1.  #读取数据  2.  batch_size = 50  3.  # 将训练数据的特征和标签组合  4.  dataset = Data.TensorDataset(trainfeatures, trainlabels)  5.  # 把 dataset 放入 DataLoader  6.  train_iter = Data.DataLoader(  7.      dataset=dataset, # torch TensorDataset format  8.      batch_size=batch_size, # mini batch size  9.      shuffle=True, # 是否打乱数据 (训练集一般需要进行打乱)  10.      num_workers=0, # 多线程来读数据, 注意在Windows下需要设置为0  11.  )  12.  # 将测试数据的特征和标签组合  13.  dataset = Data.TensorDataset(testfeatures, testlabels)  14.  # 把 dataset 放入 DataLoader  15.  test_iter = Data.DataLoader(  16.      dataset=dataset, # torch TensorDataset format  17.      batch_size=batch_size, # mini batch size  18.      shuffle=True, # 是否打乱数据 (训练集一般需要进行打乱)  19.      num_workers=0, # 多线程来读数据, 注意在Windows下需要设置为0  20.  )
复制代码


注:因为统一用的是 torch 中自带的模块 Data.TensorDataset,其过程和上一部分的第 1.1.3 部分一样。1.2.4 初始化参数


1.  #初始化参数  2.  num_hiddens,num_outputs = 256,1  3.    4.  W1 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens,num_inputs)), dtype=torch.float32)  5.  b1 = torch.zeros(1, dtype=torch.float32)  6.  W2 = torch.tensor(np.random.normal(0, 0.01, (num_outputs,num_hiddens)), dtype=torch.float32)  7.  b2 = torch.zeros(1, dtype=torch.float32)  8.  params =[W1,b1,W2,b2]  9.  for param in params:  10.      param.requires_grad_(requires_grad=True)  
复制代码


注:这部分和 4.1.1 的第 4)部分大致一样,因为网络的大体模型(如:隐藏的个数、隐藏单元的个数、以及最后的输出层神经元个数)是一样的,只是输入层的特征不同而已 1.2.5 定义模型


1.  #定义模型  2.  def net(X):  3.      X = X.view((-1,num_inputs))  4.      H = relu(torch.matmul(X,W1.t())+b1)  5.      return torch.matmul(H,W2.t())+b2  
复制代码


注:这里的激活函数我设计的是 ReLU 激活函数,因为其 x>0 部分的不存在梯度消失等问题,对于隐藏的单元的激活函数普遍使用的是 ReLU 激活函数。1.2.6 定义交叉熵损失函数和优化器


1.  #定义模型  2.  def net(X):  3.      X = X.view((-1,num_inputs))  4.      H = relu(torch.matmul(X,W1.t())+b1)  5.      return torch.matmul(H,W2.t())+b2  
复制代码


注:作为输出层的 Sigmoid 激活函数,这里我利用 torch.nn.BCEWithLogitsLoss()函数,它将 Sigmoid 函数和交叉熵损失函数合并在了一起计算,相比于单独计算更加稳定了,然后同样是小批量随机梯度下降法进行梯度更新。1.2.7 定义模型训练函数


1.  #定义模型训练函数  2.  def train(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,optimizer=None):  3.      train_ls = []  4.      test_ls = []  5.      for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期  6.          train_l_sum, train_acc_num,n = 0.0,0.0,0  7.          # 在每一个迭代周期中,会使用训练数据集中所有样本一次  8.          for X, y in train_iter: # x和y分别是小批量样本的特征和标签  9.              y_hat = net(X)  10.              l = loss(y_hat, y.view(-1,1)) # l是有关小批量X和y的损失  11.              #梯度清零  12.              if optimizer is not None:  13.                  optimizer.zero_grad()  14.              elif params is not None and params[0].grad is not None:  15.                  for param in params:  16.                      param.grad.data.zero_()  17.              l.backward() # 小批量的损失对模型参数求梯度  18.              if optimizer is None:  19.                  SGD(params,lr,batch_size)  20.              else:  21.                  optimizer.step()  22.              #计算每个epoch的loss  23.              train_l_sum += l.item()*y.shape[0]  24.              #train_acc_num += (y_hat.argmax(dim=1)==y).sum().item()  25.              n+= y.shape[0]  26.          test_labels = testlabels.view(-1,1)  27.          train_ls.append(train_l_sum/n)  28.          test_ls.append(loss(net(testfeatures),test_labels).item())  29.          print('epoch %d, train_loss %.6f,test_loss %.6f'%(epoch+1, train_ls[epoch],test_ls[epoch]))  30.      return train_ls,test_ls  
复制代码


注:这里不同于之前的“手动实现前馈神经网络解决回归”问题,在训练集的误差上,是在每次 epoch 下的损失和的平均,而回归之前用的是每次 epoch 之后(即参数更新后)进行的训练集的损失和的平均,我感觉对于回归其 loss 计算应该也是在每次 epoch 下的损失和平均,但是对于 loss 的趋势来说,这样是没有影响的;这里的训练集误差需要注意的是损失函数求得是这个小批量的平均值,而一般最后一个迭代的个数可能会比之前的小,所以这里需要把平均值全部展开,计算所有的损失的和然后除以总的训练集个数,对于测试集的 loss,其是在每次 epoch 梯度更新后计算的,是对于所有测试集的平均值,符合条件,最后存入列表中返回。1.2.8 开始训练模型


1.  lr = 0.01  2.  num_epochs = 50  3.  train_loss,test_loss = train(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr)  
复制代码


注:调用模型函数,定义好训练次数、学习率等参数进行训练,返回结果保存到列表中进行画图。这里的 loss 相比于之前的回归问题比较小,其解释在实验结果部分。1.2.9 绘制训练集和测试集的 loss 曲线


1.  lr = 0.05  2.  num_epochs = 500  3.  train_loss,test_loss = train(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr)  
复制代码


注:调用之前的模型函数,传入参数:学习率,迭代次数,批量大小等,进行训练。1.2.10 绘制训练集和测试集的 loss 曲线


1.  x = np.linspace(0,len(train_loss),len(train_loss))  2.  plt.plot(x,train_loss,label="train_loss",linewidth=1.5)  3.  plt.plot(x,test_loss,label="test_loss",linewidth=1.5)  4.  plt.xlabel("epoch")  5.  plt.ylabel("loss")  6.  plt.legend()  7.  plt.show()  
复制代码


注:这里也是借鉴 ppt 的绘图部分,主要是利用 matplotlib 库,进行绘制,横坐标是迭代次数,纵坐标分别之前保存的训练集误差和测试集误差。


1.3“手动实现前馈神经网络解决多分类”实验实验过程:1.3.1 导入所需要的包


1.  import torch  2.  import numpy as np  3.  import random  4.  from IPython import display  5.  from matplotlib import pyplot as plt  6.  import torchvision  7.  import torchvision.transforms as transforms   
复制代码


1.3.2 下载 MNIST 数据集


1.  #下载MNIST手写数据集  2.  mnist_train = torchvision.datasets.MNIST(root='./Datasets/MNIST', train=True,  3.  download=True, transform=transforms.ToTensor())  4.  mnist_test = torchvision.datasets.MNIST(root='./Datasets/MNIST', train=False,  5.  download=True, transform=transforms.ToTensor())  
复制代码


注:按实验要求,这里利用 torchvision 模块下载了数字手写数据集,其中训练集为 60000 张图片,测试集为 10000 张图片,其每个图片对应的标签是 0-9 之间,分别代表手写数字 0,1,2,3,4,5,6,7,8,9.1.3.3 读取数据


1.  #读取数据  2.  batch_size = 32  3.  train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True,  4.  num_workers=0)  5.  test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False,  6.  num_workers=0)  
复制代码


1.3.4 初始化参数+定义隐藏层的激活函数


1.  #初始化参数  2.  num_hiddens,num_outputs = 256,1  3.    4.  W1 = torch.tensor(np.random.normal(0, 0.01, (num_hiddens,num_inputs)), dtype=torch.float32)  5.  b1 = torch.zeros(1, dtype=torch.float32)  6.  W2 = torch.tensor(np.random.normal(0, 0.01, (num_outputs,num_hiddens)), dtype=torch.float32)  7.  b2 = torch.zeros(1, dtype=torch.float32)  8.  params =[W1,b1,W2,b2]  9.  for param in params:  10.      param.requires_grad_(requires_grad=True)  11.    12.  def relu(x):  13.      x = torch.max(input=x,other=torch.tensor(0.0))  14.      return x  
复制代码


注:因为网络的大体模型(如:隐藏的个数、隐藏单元的个数、以及最后的输出层神经元个数)是一样的,只是输入层的特征不同,这里为 784;隐藏层同为 ReLU 激活函数,所以在手动实现前馈神经网络中,主要差距在后面的模型定义中。1.3.5 定义模型


1.  #定义模型  2.  def net(X):  3.      X = X.view((-1,num_inputs))  4.      H = relu(torch.matmul(X,W1.t())+b1)  5.      return torch.matmul(H,W2.t())+b2  
复制代码


1.3.6 定义交叉熵损失函数和优化器


1.  #定义多分类交叉熵损失函数  2.  loss = torch.nn.CrossEntropyLoss()  3.    4.  #定义随机梯度下降法  5.  def SGD(paras,lr):  6.      for param in params:  7.          param.data -= lr * param.grad  
复制代码


注:可以看到 5)中的定义的模型和之前的是一样的,作为输出层的 Softmax 激活函数,这里我利用 torch.nn. CrossEntropyLoss()函数,它将 Softmax 函数和交叉熵损失函数合并在了一起计算,同理相比于单独计算更加稳定了,然后同样是小批量随机梯度下降法进行梯度更新。1.3.7 定义测试集 loss 以及准确率


1.  #测试集loss  2.  def evaluate_loss(data_iter,net):  3.      acc_sum,loss_sum,n = 0.0,0.0,0  4.      for X,y in data_iter:  5.          y_hat = net(X)  6.          acc_sum += (y_hat.argmax(dim=1)==y).sum().item()  7.          l = loss(y_hat,y) # l是有关小批量X和y的损失  8.          loss_sum += l.sum().item()*y.shape[0] 9.          n+=y.shape[0]  10.      return acc_sum/n,loss_sum/n  
复制代码


注:这里不同于之前的二分类和回归问题,在这里我加入对测试集的准确率的判断,利用测试集的预测值和真实值进行比较,计算测试准确的概率,其中对测试集的 loss 求法.1.3.8 定义训练函数


1.  定义模型训练函数  2.  def train(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,optimizer=None):  3.      train_ls = []  4.      test_ls = []  5.      for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期  6.          train_l_sum, train_acc_num,n = 0.0,0.0,0  7.          # 在每一个迭代周期中,会使用训练数据集中所有样本一次  8.          for X, y in train_iter: # x和y分别是小批量样本的特征和标签  9.              y_hat = net(X)  10.              l = loss(y_hat, y).sum() # l是有关小批量X和y的损失  11.              #梯度清零  12.              if optimizer is not None:  13.                  optimizer.zero_grad()  14.              elif params is not None and params[0].grad is not None:  15.                  for param in params:  16.                      param.grad.data.zero_()  17.              l.backward() # 小批量的损失对模型参数求梯度  18.              if optimizer is None:  19.                  SGD(params,lr)  20.              else:  21.                  optimizer.step()  22.              #计算每个epoch的loss  23.              train_l_sum += l.item() *y.shape[0] 24.              #计算训练样本的准确率  25.              train_acc_num += (y_hat.argmax(dim=1)==y).sum().item()  26.              #每一个epoch的所有样本数  27.              n+= y.shape[0]  28.          train_ls.append(train_l_sum/n)  29.          test_acc,test_l = evaluate_loss(test_iter,net)  30.          test_ls.append(test_l)  31.          print('epoch %d, train_loss %.6f,test_loss %f,train_acc %.6f,test_acc %.6f'%(epoch+1, train_ls[epoch],test_ls[epoch],train_acc_num/n,test_acc))  32.      return train_ls,test_ls  
复制代码


注:这里不同于之前的回归和二分类,我在这里引入对训练集和测试集的准确率的计算,即在训练集和测试集上分别判别预测值和真实值是否相等,统计预测准确的数量占总体数量的百分比,并将最后结果打印出来,其他的过程都是一样的,就不加赘述。1.3.9 开始训练模型


1.  lr = 0.01  2.  num_epochs = 50  3.  train_loss,test_loss = train(net,train_iter,test_iter,loss,num_epochs,batch_size,params,lr)   
复制代码


注:调用模型函数,定义好训练次数、学习率等参数进行训练,返回结果保存到列表中进行画图。1.3.10 绘制训练集和测试集的 loss 曲线


1.  x = np.linspace(0,len(train_loss),len(train_loss))  2.  plt.plot(x,train_loss,label="train_loss",linewidth=1.5)  3.  plt.plot(x,test_loss,label="test_loss",linewidth=1.5)  4.  plt.xlabel("epoch")  5.  plt.ylabel("loss")  6.  plt.legend()  7.  plt.show()  
复制代码


注:这里也是借鉴 ppt 的绘图部分,主要是利用 matplotlib 库,进行绘制,横坐标是迭代次数,纵坐标分别之前保存的训练集误差和测试集误差。


实现代码下载:https://download.csdn.net/download/qq_37534947/13080074数据集下载:https://download.csdn.net/download/qq_37534947/13080094torch 实现请参考:https://blog.csdn.net/qq_37534947/article/details/109402126

发布于: 刚刚阅读数: 4
用户头像

Studying_swz

关注

还未添加个人签名 2020-12-23 加入

还未添加个人简介

评论

发布
暂无评论
pytorch实现前馈神经网络实验(手动实现)_6 月优质更文活动_Studying_swz_InfoQ写作社区