1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
# coding=utf-8
from mxnet import gluon
from mxnet import ndarray as nd
#这里我们用MNIST分类数字
# 我们通过gluon的data.vision模块自动下载这个数据。
def transform(data, label):
return data.astype('float32')/255, label.astype('float32')
mnist_train = gluon.data.vision.MNIST(train=True, transform=transform)
mnist_test = gluon.data.vision.MNIST(train=False, transform=transform)
# 打印一个样本的形状和它的标号
data, label = mnist_train[0]
print ('example shape: ', data.shape, 'label:', label) ##('example shape: ', (28L, 28L, 1L), 'label:', 5.0)
## 数据读取
#虽然我们可以像前面那样通过yield来定义获取批量数据函数,
# 这里我们直接使用gluon.data的DataLoader函数,它每次yield一个批量。
# 注意到这里我们要求每次从训练数据里读取一个由随机样本组成的批量,
# 但测试数据则不需要这个要求。
batch_size = 256
train_data = gluon.data.DataLoader(mnist_train, batch_size, shuffle=True)
test_data = gluon.data.DataLoader(mnist_test, batch_size, shuffle=False)
## 初始化模型参数
# 跟线性模型一样,每个样本会表示成一个向量。我们这里数据是 28 * 28 大小的图片,
# 所以输入向量的长度是 28 * 28 = 784。
# 因为我们要做多类分类,我们需要对每一个类预测这个样本属于此类的概率。
# 因为这个数据集有10个类型,所以输出应该是长为10的向量。
# 这样,我们需要的权重将是一个 784 * 10 的矩阵:
num_inputs = 784
num_outputs = 10
W = nd.random_normal(shape=(num_inputs, num_outputs))
b = nd.random_normal(shape=num_outputs)
params = [W, b]
#同之前一样,我们要对模型参数附上梯度:
for param in params:
param.attach_grad()
## 定义模型
# 在线性回归教程里,我们只需要输出一个标量yhat使得尽可能的靠近目标值。
# 但在这里的分类里,我们需要属于每个类别的概率。
# 这些概率需要值为正,而且加起来等于1.
# 而如果简单的使用 Y=WX,我们不能保证这一点。
# 一个通常的做法是通过softmax函数来将任意的输入归一化成合法的概率值。
from mxnet import nd
def softmax(X):
exp = nd.exp(X) ##全部变为正的
# 假设exp是矩阵,这里对行进行求和,并要求保留axis 1,
# 就是返回 (nrows, 1) 形状的矩阵
partition = exp.sum(axis=1, keepdims=True)
return exp / partition ##每个行除以它的行的和
#可以看到,对于随机输入,我们将每个元素变成了非负数,而且每一行加起来为1。
X = nd.random_normal(shape=(2,5))
print X
# [[ 0.79687113 0.85240501 0.61860603 0.47654876 0.74863517]
# [ 0.55032933 -0.22566749 -2.11320662 -0.95748073 0.32560727]]
X_prob = softmax(X)
print(X_prob)
# [[ 0.21869159 0.23117994 0.18298376 0.1587515 0.20839317]
# [ 0.39214477 0.1804826 0.02733301 0.08681861 0.31322101]]
print(X_prob.sum(axis=1)) # [ 0.99999994 1. ] ([1,1])
#现在我们可以定义模型了:
def net(X):
return softmax(nd.dot(X.reshape((-1,num_inputs)), W) + b)
## 交叉熵损失函数
# 我们需要定义一个针对预测为概率值的损失函数。其中最常见的是交叉熵损失函数,
# 它将两个概率分布的负交叉熵作为目标值,最小化这个值等价于最大化这两个概率的相似度。
# 具体来说,我们先将真实标号表示成一个概率分布,例如如果y=1,
# 那么其对应的分布就是一个除了第二个元素为1其他全为0的长为10的向量,
# 也就是 yvec=[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]。
# 那么交叉熵就是yvec[0]*log(yhat[0])+...+yvec[n]*log(yhat[n])。
# 注意到yvec里面只有一个1,那么前面等价于log(yhat[y])。
# 所以我们可以定义这个损失函数了
def cross_entropy(yhat, y):
return - nd.pick(nd.log(yhat), y)
#给定一个概率输出,我们将预测概率最高的那个类作为预测的类,
# 然后通过比较真实标号我们可以计算精度:
def accuracy(output, label):
return nd.mean(output.argmax(axis=1)==label).asscalar()
#我们可以评估一个模型在这个数据上的精度。
def evaluate_accuracy(data_iterator, net):
acc = 0.
for data, label in data_iterator:
output = net(data)
acc += accuracy(output, label)
return acc / len(data_iterator)
#因为我们随机初始化了模型,所以这个模型的精度应该大概是1/num_outputs = 0.1.
print evaluate_accuracy(test_data, net) #0.09814453125
def SGD(params, lr):
for param in params:
param[:] = param - lr * param.grad
## 训练
import sys
sys.path.append('..')
from mxnet import autograd
learning_rate = .1
for epoch in range(5):
train_loss = 0.
train_acc = 0.
for data, label in train_data:
with autograd.record():
output = net(data)
loss = cross_entropy(output, label)
loss.backward()
# 将梯度做平均,这样学习率会对batch size不那么敏感
SGD(params, learning_rate/batch_size)
train_loss += nd.mean(loss).asscalar()
train_acc += accuracy(output, label)
test_acc = evaluate_accuracy(test_data, net)
print("Epoch %d. Loss: %f, Train acc %f, Test acc %f" % (
epoch, train_loss/len(train_data), train_acc/len(train_data), test_acc))
## 预测
#训练完成后,现在我们可以演示对输入图片的标号的预测
data, label = mnist_test[0:9]
print('true labels')
print(label)
predicted_labels = net(data).argmax(axis=1)
print('predicted labels')
print(predicted_labels.asnumpy())
|