本系列的上一篇博文最后提出了一个问题,是有关如何通过torch来实现给定的神经网络的,这里公布一下我自己的回答:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.layer12 = nn.Linear(784, 200)
self.layer23 = nn.Linear(200, 100)
self.layer34 = nn.Linear(100, 10)
def forward(self, input):
output = self.layer12(input)
output = torch.relu(output)
output = self.layer23(output)
output = torch.relu(output)
output = self.layer34(output)
output = nn.functional.softmax(output)
return output
是不是感觉很简单呢,先别着急,构建一个神经网络容易,但是要训练其中的参数是很麻烦的。
此篇博文将以sklearn中的手写数字图像作为数据集,来讲述一个神经网络是如何训练和测试的。
Python scikit-learn库中有一个datasets模块,此模块中收录了很多经典的训练模型用到的数据集,mnist(手写数字图像,分辨率8*8)便是其中之一。本篇博文便是基于此数据集对图片中的手写数字进行识别和分类,此数据集是神经网络初学者的必经之路。
载入这些数字图像的方法很简单,详见以下代码:
def preprocess_digit():
data = load_digits()
x, y = data.data, data.target
## Input Normalization
x = MinMaxScaler().fit_transform(x)
return train_test_split(x, y, test_size=0.1)
一般来说,在训练分类型神经网络模型时,输入的数据是要进行标准化的,这在以上代码中的“x = MinMaxScaler().fit_transform(x)”有所体现。本人在此之前未对数据进行标准化的预处理,造成了训练时参数的爆炸…(沉痛的教训,宝贵的经验)
把上面的函数进行调用,执行下面代码,可以得到相关的数据集的信息:
xtrain, xtest, ytrain, ytest = preprocess_digit()
print(xtrain.shape)
print(xtest.shape)
print(ytrain.shape)
print(ytest.shape)
此代码可以查看数据量的大小:
说明训练集数据有1617张图像,测试集有180张图像,所有图像的大小为8_8,但是载入的数据是1_64的行向量。
之前讲过这种代码该如何写,不多说,直接上代码:
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.layer12 = nn.Linear(64, 50)
self.layer23 = nn.Linear(50, 25)
self.layer34 = nn.Linear(25, 10)
def forward(self, input):
output = self.layer12(input)
output = torch.relu(output)
output = self.layer23(output)
output = torch.relu(output)
output = self.layer34(output)
output = nn.functional.softmax(output)
return output
这里通过实例代码来阐述torch是如何进行网络的训练的,首先先贴出我训练成功时所用的代码:
def train():
network = Net()
optimizer, loss_func = optim.SGD(network.parameters(), lr=0.5), nn.MSELoss()
for epoch in range(len(y_train)):
x = Variable(torch.tensor(x_train[epoch], dtype=torch.float32)).reshape([1, 64])
y = torch.zeros([1, 10])
y[0, y_train[epoch]] = 1.0
for i in range(10):
prediction = network(x)
loss = loss_func(prediction, y)
optimizer.zero_grad() ## Clear to zero for the devitation of loss(loss-weight)
loss.backward()
optimizer.step()
一行一行来看:
相当,等效于在此定义了Loss函数的形式。(yhat意思是y的估计,Loss函数是y估计与y差值的二范数平方)
以上便是利用torch训练一个神经网络的相关步骤,在数据集很大的情况下,训练的步骤耗时很大,建议程序运行时做点别的事~
保存网络参数很简单,直接上代码
if not os.path.isdir('model'):
os.mkdir('model')
torch.save(network, './model/full_connected.cpkt')
代码的逻辑很容易看懂,当model文件夹不存在时,创建model文件夹,然后在这个文件夹里面保存网络参数。
torch.save()可以保存的参数种类有很多,除了上面代码里面的类类型network以外,还有torch.Tensor、字典、参数列表network.parameters()等。
网络参数保存为文件之后,再次使用时就需要下面代码读取模型参数:
network = torch.load('./model/full_connected.cpkt')
network.eval()
也很容易理解,在torch.load中添加文件的路径即可,但是你事先要知道这个文件中存储的参数类型,并且要用正确的变量类型来接收参数读取的结果。
直接给出代码:
for epoch in range(len(y_test)):
x = Variable(torch.tensor(x_test[epoch], dtype=torch.float32)).reshape([1, L])
prediction = network(x)
max = torch.max(prediction).detach().numpy()
if max == prediction[0, y_test[epoch]].detach().numpy():
count += 1
print("The accuracy of the testSet(%s) : %2.2f
" % (set, (count/len(y_test))))
和训练时的做法类似,只是此时没有了反向传播步骤,当我们得到最终的预测向量prediction作为输出时,prediction中最大元素所在的下标便是预测的结果,例如,如果最大元素所在下标为8,则程序识别出来的数字是8。prediction是一个长度为10的向量,原因就是0~9包含了10个数字,prediction本质上存储的是每个数字被识别的概率,其中概率最大的就是机器识别的结果,这是神经网络用于分类问题时普遍采用的策略。
以上的图片给出了识别正确的数字的例子。
这里打印的是模型识别数字的准确率,可以发现,识别的准确率还算可以,由于本人在这里采用的是随机梯度下降法进行参数更新,所以网络有点过拟合。一般的情况下,在训练网络时都是优先采用批量梯度下降(BGD)法进行网络训练,这里只是做训练的简单演示,所以未考虑此问题。
所有与全连接前馈神经网络有关的代码都在下列的Gitee链接中,本人在今后还会向此代码库增加与神经网络相关的内容。
神经网络代码Gitee链接
如果要用神经网络进行非线性回归,代码应该怎么写,你有大致的思路吗?