1 历经30年的阿拉伯数字学习
爸爸妈妈拿出 0 ~ 9 这10张数字图片……
爸爸妈妈: 这是0,这是1,这是2……这是9
爸爸妈妈又紧接着测试我……
爸爸妈妈: 宝宝这10个数字你都认识了么?
我感觉那个 0 似乎和大苹果有点像,圆圆的,其他的……¥#@@#¥……都是什么玩意儿!
宝宝(我): 0、#¥%@。
爸爸妈妈很有耐心,又教了宝宝一遍。
再次测试
这次宝宝认出了2个数字!
日复一日,年复一年……
爸爸妈妈无数次地重复教我这10个数字……
30年后……
笔者终于可以分辨 0~9 这 10 个数字了!😶
耶!😄
这个故事再次告诉我们:
重复的学习是有效的!(呃……即便要30年……)
所以,当老师再让我们将背不会的课文抄写100遍的时候,不要再反抗了哦!
2 算法是重复学习的成果
上一篇当中👆 有小伙伴就产生了疑问👇
image.png我们提到的算法由 网络 和 权重(连线) 组成👇
所谓的算法然而:
网络,未说明为什么网络是这样;
权重,未说明值是怎么来的……
呃……说起来这算法来的确实有点突兀……
但同样的问题如果你问机器,ta会告诉你说:算法是重复学习的成果
先给权重赋一些随机值,然后不停地将训练集(那10张标准数字)输入给算法。算法正确识别正确了,我们告诉算法这是对的,要继续这么做;算法识别错误了,我们告诉算法识别错误了,然后给出正确的答案,让算法后续改正。
貌似……机器真的可以像人一样……学习?
3 让机器学习
我们只需要4步,即可让机器自行学习。
3.1 定义并加载网络
比如上篇我们提到的 FullConnect 操作,我们用 pytorch 中的 Linear 操作来模拟它,并将它作为网络的唯一操作。
nn.Linear(25, 10, bias=False)
25:即输入25个值(5x5个小纸片)
10:即输出10个值(输入为0~9这10个数字的可能性)
bias=False:不需要偏置参数,可暂时不关注。
pytroch 的 Linear官方算法说明
感兴趣的小伙伴可以参考哦👆👇
3.2 构造并加载数据
我们选择的训练集、测试集数据如下👇
训练集 测试集和上篇一致,这次就不解释咯~
3.3 设置训练策略
诸如 学习率、损失函数 的选择配置,此篇我们暂不详细说明(以后再讲),目前知道有这个东西就好啦。
3.4 训练
来来,大训 300 回合!
训练 300轮
即:epoch = 300
即:让算法学习训练集的每个数字 300次
即:训练过程要执行算法 10 * 300 = 3000次
以上4步即可完成一个我们数字识别模型的训练。
4 训练结果
4.1 训练集10张图、测试集3张图、训练30000轮
训练集、测试集的准确率随着训练轮数的变换如下:
训练到59轮,训练集完全识别 训练到300轮,测试集识别率66.7% 训练到3000轮,测试集识别率66.7% 训练到30000轮,测试集识别率66.7%我们发现,训练到第 59轮 时,我们的算法识别率对训练集、测试集已达到是 100%、66.7%,但训练到第 300轮、3000轮、30000轮时……准确率依然如此,测试集的识别率始终无法提升到100%。
5.2 训练集13张图,训练300轮
但如果我们将 3张 测试集的数据也交给机器学习呢?即 训练13张图片?
第209轮达到100%的识别率我们发现,在进行到第 209轮 的时候,所有 13张 图片已经可以完全被机器识别了。
我们来看看机器学到的权重数据。
再看看笔者上篇给出的权重数据:
笔者上篇直接赋值的权重数据复杂度不在一个层面上……
5 代码
那么,这样一个包含 网络定义、训练集、测试集、训练、模型准确率计算 的完整训练代码,一定很复杂吧?不,它不到 150行!而且只依赖 python环境 和 pytorch、numpy 两个 python工具包,嗯……值得亲自跑跑看!👇
# coding: UTF-8
import torch
import torch.nn as nn
import numpy as np
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.full_connect = nn.Linear(25, 10, bias=False)
def forward(self, x):
out = self.full_connect(x);
return out;
def load_train_data():
tdatas_tensor = torch.from_numpy(train_array).float()
tlabels_tensor = torch.from_numpy(train_label).long()
return tdatas_tensor, tlabels_tensor
def load_test_data():
tdatas_tensor = torch.from_numpy(test_array).float()
tlabels_tensor = torch.from_numpy(test_label).long()
return tdatas_tensor, tlabels_tensor
def batch_train_one_epock(model, batch_num, train_data, train_label, loss_func, optimizer):
model.train()
train_loss = 0.
train_acc = 0.
loop_num = int(len(train_data) / batch_num)
for i in range(loop_num):
# 1 获取 训练数据 & 训练Label
batch_input = train_data[i * batch_num: ((i + 1) * batch_num)]
batch_label = train_label[i * batch_num: ((i + 1) * batch_num)]
# 2 执行推理
batch_out = model(batch_input)
# 3 计算模型损失
batch_loss = loss_func(batch_out, batch_label)
train_loss += batch_loss.data
# 4 模型推理准确值累加(用以计算准确率)
pred = torch.max(batch_out, 1)[1]
train_correct = (pred == batch_label).sum()
train_acc += train_correct.data
# 5 适时打印信息
# print("第", epoch + 1, "轮,已训练", i * batch_num, "项,该批Loss:", np.around(batch_loss.data.numpy(), decimals=6));
# 6 反馈更新权重
optimizer.zero_grad()
batch_loss.backward()
optimizer.step()
train_num = loop_num * batch_num
return train_loss, train_acc, train_num
def batch_test(model, batch_num, test_data, test_label, loss_func, optimizer):
model.eval()
eval_loss = 0.
eval_acc = 0.
loop_num = int(len(test_data) / batch_num)
for i in range(loop_num):
# 1 获取 训练数据 & 训练Label
batch_input = test_data[i * batch_num: ((i + 1) * batch_num)]
batch_label = test_label[i * batch_num: ((i + 1) * batch_num)]
# 2 执行推理
batch_out = model(batch_input)
# 3 计算模型损失
batch_loss = loss_func(batch_out, batch_label)
eval_loss += batch_loss.data
# print(np.around(eval_loss.data.numpy(), decimals=3));
# 4 模型推理准确值累加(用以计算准确率)
pred = torch.max(batch_out, 1)[1]
num_correct = (pred == batch_label).sum()
eval_acc += num_correct.data
test_num = loop_num * batch_num
return eval_loss, eval_acc, test_num
# 训练集
train_array = np.array( \
[[0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0], \
[0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0], \
[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0], \
[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0], \
[0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0], \
[0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0], \
[0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0], \
[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0], \
[0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0], \
[0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0], \
[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0], \
[0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0], \
[0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0] \
])
# 训练集标注
train_label = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9 \
, 5, 5, 5
])
# 测试集
test_array = np.array( \
[[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0], \
[0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0], \
[0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0]])
# 测试集标注
test_label = np.array([5, 5, 5]);
# 1 加载模型
model = Net();
# 2 加载训练数据
train_data = torch.from_numpy(train_array).float()
train_label = torch.from_numpy(train_label).long()
# 3 加载测试数据
test_data = torch.from_numpy(test_array).float()
test_label = torch.from_numpy(test_label).long()
# 4 设置训练策略
optimizer = torch.optim.Adam(model.parameters())
loss_func = torch.nn.CrossEntropyLoss()
# 5 训练300轮
for epoch in range(300):
# 单轮训练
train_loss, train_acc, train_num = batch_train_one_epock(model, 1, train_data, train_label, loss_func, optimizer)
print('第', epoch + 1, '轮 ', 'Train Loss: {:.6f}, Acc: {:.6f}'.format(train_loss / train_num, train_acc.float() / train_num))
# 每轮测试准确率
test_loss, test_acc, test_num = batch_test(model, 1, test_data, test_label, loss_func, optimizer)
print('第', epoch + 1, '轮 ', 'Test Loss: {:.6f}, Acc: {:.6f}'.format(test_loss / test_num, test_acc.float() / test_num))
# print(model.state_dict());
6 结束 or 开始?
我们已经可以训练模型了哎!那么AI灭神计划是不是已经到尾声了呢?不,我们没有解释的问题还很多很多!将AI拉下神坛,才刚刚开始!
网友评论