好的,我们开始吧。首先第一个问题,神经网络的本质是什么?是古典主义的人类的神经元吗?绝对不是,他只是一个优化函数
y = f θ ( x ) y = f_{\theta}(x) y=fθ(x)
这和小学学到的线性函数拟合并无本质区别。只是其中参数 θ \theta θ是大量的参数,并且函数 f θ f_{\theta} fθ本身的形式也会非常复杂。相应的参数 θ \theta θ也无法像线性拟合一样,直接列方程求解,而是依赖于优化算法。
所以今天,我们就尝试用神经网络,去学习几个函数表达式。
训练代码解读
不多说了直接贴代码
import numpy as np
import math
import matplotlib.pyplot as pltimport torch
import torch.nn as nn
import torch.optim as optimclass Net(nn.Module):def __init__(self, num_layers, input_size, hidden_size, output_size):super(Net, self).__init__()self.layers = nn.ModuleList([nn.Linear(input_size, hidden_size, bias=True)] + [nn.Linear(hidden_size, hidden_size, bias=True) for _ in range(num_layers - 2)] + [nn.Linear(hidden_size, output_size, bias=True)])def forward(self, x):for layer in self.layers:x = nn.functional.gelu(layer(x))return xif __name__=="__main__":# 被训练函数---------------------------x = np.linspace(0.1, 2.1, 100)y = [[math.exp(ele)*ele, math.exp(ele), ele*ele] for ele in x]plt.plot(x, y)plt.show()# 训练部分------------------------# 生成被训练数据------------------------x = torch.Tensor(x.reshape(-1,1))y = torch.Tensor(y)# 初始化网络net = Net(6, 1, 32, 3)# 定义损失函数和优化器loss_fn = nn.MSELoss(reduction='sum')optimizer = optim.Adam(net.parameters(), lr=0.01)# 训练模型epochs = 1000for epoch in range(epochs):y_pred = net(x)loss = loss_fn(y_pred, y)optimizer.zero_grad() #梯度清零,否则梯度会累积loss.backward() #计算参数关于loss函数的梯度,需要做梯度会穿optimizer.step() #利用梯度对model参数进行一次训练if epoch % 10 == 0:print('epoch: ', epoch, " loss: ", loss.item())if epoch % 100 == 0:plt.clf()plt.plot(x, y,'b-',x, y_pred.detach().numpy(),'r-')plt.show()
这里多说一句撒,关于训练的写法,我觉得mindspore设计得是比pytorch要好的。optimizer.zero_grad()这种事情,mindspore就不需要做,不如说正常人前一次梯度都不会保留吧,默认不保留才是合理的吧。第二个是,获取梯度的grad,loss.backward应该显式的写出来,然后传进优化器的一次训练步才更加符合直觉,比如像下面这样
grad = loss.backward() #梯度回传获取grad
optimizer.step(grad) #利用梯度对net的参数进行一次训练
# 上述两行纯属个人yy,不能运行的哈
训练结果如下
可以看到300左右就已经收敛得比较好了。
神经网络的局限性与正则化的重要性
简单粗暴的将被学习的函数第一个分量减去10
x = np.linspace(0.1, 2.1, 100)y = [[math.exp(ele)*ele - 10, math.exp(ele), ele*ele] for ele in x]
突然就不能学了
这其实是因为,我们使用的激活函数,为gelu函数。嗯差不多长下面这样,这种激活函数是没法表示负值的。导致整个神经网络没法表示负数。
所以说神经网络也不完全是魔法,而是一个优化问题。我们需要先找到能以较少参数就表示被优化函数的网络,然后再对这个网络里面的参数进行优化,才能得到合理的结果。对当前情况,我们选一个,能表示较大负数的激活函数,例如:呃?!我震惊的发现好像没有类似这样的激活函数,AI训练的问题大多数都是非负的。
好吧,所以神经网络要学习的问题,我们一定要做合适的正则化或者归一化,让我们的网络能够表示当前的问题才行。