动手学习深度学习1-4 softmax

Softmax 回归的从零开始实现

理解:https://blog.csdn.net/red_stone1/article/details/80687921

%matplotlib inline
import gluonbook as gb  #要把这个包放在同一个根目录下
from mxnet import autograd, nd
#============获取和读取数据使用 Fashion-MNIST 数据集,并设置批量大小为 256
batch_size = 256
train_iter, test_iter = gb.load_data_fashion_mnist(batch_size)#完成了将数据封装成数据迭代器
#这函数中有.vision.FashionMNIST()会自动下载数据还有.DataLoader()这函数是将数据弄成一个数据迭代器

#===========初始化模型参数
#跟线性回归中的例子一样,我们将使用向量表示每个样本。已知每个样本输入是高和宽均为 28 像素的图像。
#模型的输入向量的长度是  28×28=784 :该向量的每个元素对应图像中每个像素。由于图像有 10 个类别,单层神经网络输出层的输出个数为 10。
#所以 Softmax 回归的权重和偏差参数分别为 784×10  和  1×10 的矩阵。
num_inputs=784
num_outputs=10
W=nd.random.normal(scale=0.01,shape=(num_inputs,num_outputs))
b=nd.zeros(num_outputs)
W.attach_grad()  #就是为参数梯度的存放开辟了空间,以助于参数更新的时候进行访问
b.attach_grad()

#===========实现 Softmax 运算
#给定一个 NDArray 矩阵X。我们可以只对其中同一列(axis=0)或同一行(axis=1)的元素求和,
# #并在结果中保留行和列这两个维度(keepdims=True)。
# X = nd.array([[1, 2, 3], [4, 5, 6]])
# X.sum(axis=0, keepdims=True), X.sum(axis=1, keepdims=True)
# 在下面的函数中,矩阵X的行数是样本数,列数是输出个数。
# 为了表达样本预测各个输出的概率,softmax 运算会先通过exp函数对每个元素做指数运算,
# 再对exp矩阵同行元素求和,最后令矩阵每行各元素与该行元素之和相除。这样一来,最终得到的矩阵每行元素和为 1 且非负。
# 因此,该矩阵每行都是合法的概率分布。Softmax 运算的输出矩阵中的任意一行元素代表了一个样本在各个输出类别上的预测概率。
def softmax(X):
    X_exp=X.exp()
    partition=X_exp.sum(axis=1,keepdims=True)#对行求和,保留行和列
    return X_exp/partition  #这里运用了广播机制

#==================定义模型
# 有了 softmax 运算,我们可以定义上节描述的 softmax 回归模型了。
# 这里通过reshape函数将每张原始图像改成长度为num_inputs的向量
def net(X):
    return softmax(nd.dot(X.reshape((-1,num_inputs)),W)+b)
# 因为X是3d的东西要把它resize成2d的,-1是让系统自己去判断要多少行其实这就是batch_size


#======================定义损失函数
# softmax 回归使用的交叉熵损失函数。为了得到标签的预测概率,我们可以使用pick函数。
# 在下面例子中,变量y_hat是 2 个样本在 3 个类别的预测概率,变量y是这 2 个样本的标签类别。
# 通过使用pick函数,我们得到了 2 个样本的标签的预测概率。
# 与“Softmax 回归”一节数学表述中标签类别离散值从 1 开始逐一递增不同,在代码中,标签类别的离散值是从 0 开始逐一递增的。
# y_hat = nd.array([[0.1, 0.3, 0.6], [0.3, 0.2, 0.5]])
# y = nd.array([0, 2])
# nd.pick(y_hat, y)
#定义交叉熵损失函数
def cross_entropy(y_hat,y):
    return -nd.pick(y_hat,y).log()   #这里pick是取正确的那类对应的输出的log probability


#===================计算分类准确率
# 给定一个类别的预测概率分布y_hat,我们把预测概率最大的类别作为输出类别。
# 如果它与真实类别y一致,说明这次预测是正确的。分类准确率即正确预测数量与总预测数量之比。
# 下面定义准确率accuracy函数。其中y_hat.argmax(axis=1)返回矩阵y_hat每行中最大元素的索引,
# 且返回结果与变量y形状相同。我们在“数据操作”一节介绍过,相等条件判断式(y_hat.argmax(axis=1) == y)
# 是一个值为 0(相等为假)或 1(相等为真)的 NDArray。由于标签类型为整数,我们先将变量y变换为浮点数再进行相等条件判断。
def accuracy(y_hat,y):
    return (y_hat.argmax(axis=1)==y.astype('float32')).mean().asscalar()  #asscalar()将向量变成标量

# 让我们继续使用在演示pick函数时定义的变量y_hat和y,并将它们分别作为预测概率分布和标签。
# 可以看到,第一个样本预测类别为 2(该行最大元素 0.6 在本行的索引为 2),与真实标签 0 不一致;
# 第二个样本预测类别为 2(该行最大元素 0.5 在本行的索引为 2),与真实标签 2 一致。因此,这两个样本上的分类准确率为 0.5。
# accuracy(y_hat, y)

# 类似地,我们可以评价模型net在数据集data_iter上的准确率。
def evaluate_accuracy(data_iter, net):
    acc = 0
    for X, y in data_iter:
        acc += accuracy(net(X), y)
    return acc / len(data_iter)
# 因为我们随机初始化了模型net,所以这个随机模型的准确率应该接近于类别个数 10 的倒数 0.1。
# evaluate_accuracy(test_iter, net)


#=============训练模型
# 训练 softmax 回归的实现跟前面介绍的线性回归中的实现非常相似。我们同样使用小批量随机梯度下降来优化模型的损失函数。
# 在训练模型时,迭代周期数num_epochs和学习率lr都是可以调的超参数。改变它们的值可能会得到分类更准确的模型。
num_epochs,lr=5,0.1
def train_ch3(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,trainer=None):
    for epoch in range(num_epochs):
        train_l_sum=0
        train_acc_sum=0
        for X,y in train_iter:          #X是一个被分成batch_size=256的样本 ,也就是说这一坨对应256个标签
            with autograd.record():
                y_hat=net(X)                                 
                l=loss(y_hat,y)

            l.backward()
            if trainer is None:
                gb.sgd(params,lr,batch_size)
            else:
                trainer.setp(batch_size)#x下节会用到
            train_l_sum+=l.mean().asscalar()
            train_acc_sum += accuracy(y_hat,y)
        test_acc=evaluate_accuracy(test_iter,net)
        print('epoch %d,loss %.4f,train acc %.3f,test acc %.3f'
              %(epoch+1,train_l_sum/len(train_iter),
              train_acc_sum/len(train_iter),test_acc))
# train_ch3(net,train_iter,test_iter, cross_entropy,num_epochs,
#          batch_size,[W,b],lr)
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs,
          batch_size, [W, b], lr)
 




true_labels = gb.get_fashion_mnist_labels(y.asnumpy())
pred_labels = gb.get_fashion_mnist_labels(net(X).argmax(axis=1).asnumpy())
titles = [true + '\n' + pred for true, pred in zip(true_labels, pred_labels)]

gb.show_fashion_mnist(X[0:9], titles[0:9])

交叉熵损失函数

定义一个针对预测为概率值的损失函数。最常见的就是交叉熵损失函数,他将两个概率分布的负交叉熵作为目标值,最小化这个值等于最大化这两个概率的相似度。具体来说,我们先将真实标号表示成一个概率分布,例如y=1那么其对应的分布就是一个除了第二个元素为1其他全为0的长度为10的向量,也是就是yvec=[0,1,0,0,0,0,0,0,0,0].那么交叉熵就是yvec[0]*log(y_hat[0])+..+yvec[n]*log(y_hat[n]),也就是前面等价于log(y_hat[y])) 也就是对真实类的概率预测指出取对数。

链接:https://blog.csdn.net/red_stone1/article/details/80735068

https://zhuanlan.zhihu.com/p/27223959

注意:1.增大学习率,发现结果会变得很糟糕,精度徘徊在0.1左右

exp(x)这个函数当x很大时,他会计算出错,但mxnet不会报错,会导致精度计算不正确。

2.本节中的cross_entropy函数是按照交叉熵损失函数的数学定义实现的。这样的实现方式可能有什么问题?(提示:思考一下对数函数的定义域。)

链接:http://freemind.pluskid.org/machine-learning/softmax-vs-softmax-loss-numerical-stability/

https://www.sohu.com/a/192666279_206784

猜你喜欢

转载自blog.csdn.net/qq_36666756/article/details/83212472