softmax回归
该回归分析为后续的多层感知机做铺垫
基本概念
softmax回归用于离散模型预测(分类问题,含标签)
softmax运算本质上是对网络的多个输出进行了归一化,使结果有一个统一的判断标准,不必纠结为什么要这么算
网络结构:
- 多输入多输出,单层神经网络,输出神经元与输入全连接
- 一组输入x为n维行向量,输入矩阵为a*n,a为样本个数
- 一组输出o为m维行向量,输出矩阵为a*m
- 偏置是m维行向量
- 权重矩阵W为n*m的矩阵(n行=输入个数,m列=输出个数)
- 网络输出:
Y = softmax(O)
O = X*W + b
交叉熵损失函数
核心:
- 交叉熵只关心对正确类别的预测概率
- 最小化交叉熵损失函数 等价于 最大化似然函数,二者出自不同学科,但在数学上描述了同一个对象
- 可以使用准确率评价模型表现
完整程序及注释
完整softmax图像分类实现
import d2lzh as d2l
from mxnet import autograd, nd
'''
基础参数声明
'''
batch_size = 256
# 创建两个迭代器,用于对mnist数据集进行小批量随机采样
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)num_inputs = 28*28 # 将二维图片变成一维向量后的长度
num_outputs = 10 # 输出数量
W = nd.random.normal(loc=0, scale=0.01, shape=(num_inputs, num_outputs))
b = nd.zeros(num_outputs)
W.attach_grad()
b.attach_grad()'''
训练必要方法声明
'''
# 指数化+归一化 = softmax运算
def softmax(X):X_exp = X.exp()partition = X_exp.sum(axis=1, keepdims=True)return X_exp / partition# 将二维图片变成一维向量(改变模型形式),并为每个特征分配权重和偏置
# nd.dot(X.reshape((-1, num_inputs)), W) + b 的输出是一个矩阵,行数等于小批量中的样本数,列数等于输出的类别数量
# 每一行中数值最大的那一项,代表模型认为该样本属于这一类的概率最大
def net(X):return softmax(nd.dot(X.reshape((-1, num_inputs)), W) + b)# y是正确的标签号,y_hat是样本对各个类型的预测概率
# 该函数返回各个概率中正确标签的概率值,并进行exp()运算
def cross_entropy(y_hat, y):return -nd.pick(y_hat, y).log() # .log()方法将pick()的返回的每个元素进行exp运算# 计算样本集的准确率
def accuracy(y_hat, y):return (y_hat.argmax(axis=1) == y.astype('float32')).mean().asscalar()# 小批量对data_iter进行遍历,最后统计准确率
def evaluate_accracy(data_iter, net):acc_sum, n = 0.0, 0for X, y in data_iter: # X是特征,y是标签y = y.astype("float32")# 取出每一行中数字最大的那一项的索引号,并和标签y比较,再对该次小批量求和,累加acc_sum += (net(X).argmax(axis=1) == y).sum().asscalar()n += y.sizereturn acc_sum / n'''
训练函数
'''
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, train_acc_sum, n = 0.0, 0.0, 0for X, y in train_iter:with autograd.record():y_hat = net(X)l = loss(y_hat, y).sum() # 调用时, 传入交叉熵损失函数l.backward()if trainer == None:d2l.sgd(params, lr, batch_size)else:trainer.step(batch_size)y = y.astype("float32")train_l_sum += l.asscalar() # 总损失train_acc_sum += (y_hat.argmax(axis=1) == y).sum().asscalar() # 总准确率n += y.sizetest_acc = evaluate_accracy(test_iter, net)# 打印平均损失和平均准确率(训练集和测试集)print('epoch %d, loss %.4f, train_acc %.4f, test_acc %.4f'% (epoch+1, train_l_sum/n, train_acc_sum/n, test_acc)) '''
开始训练,一般在第9轮左右准确率逼近峰值,85%左右
想要更高的准确率,需要更好的模型和寻优算法
'''
if __name__ == "__main__":num_epochs, lr = 20, 0.1train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)
查看数据集
from matplotlib import pyplot as plt
from mxnet.gluon import data as gdata# 展示FashionMNIST数据集
mnist_train = gdata.vision.FashionMNIST(train=True)
mnist_test = gdata.vision.FashionMNIST(train=False)
print(len(mnist_train))
print(len(mnist_test))feature, label = mnist_train[19]
print(label)
img = feature
# 将 MXNet NDArray 转换为 NumPy 数组,并调整维度顺序以匹配图像格式
img_np = img.asnumpy().squeeze() # 移除单维度条目plt.imshow(img_np, cmap='gray')
plt.axis('off')
plt.show()
呈现效果
我训练了8次就手动让它中止了
具体程序解释
-
net(X)
X.reshape((-1, num_inputs))
:
这是将X重新塑形的操作。这里的reshape函数用于改变X的形状而不改变其数据。
参数(-1, num_inputs)告诉函数自动计算该维度的大小以使总元素数量保持不变,而另一维度则指定为num_inputs。
-1在这里作为一个占位符,指示系统根据原张量中的元素数量和另一个指定的维度大小自动推断出这一维度的具体值。简单来说,这段代码的作用是将输入数据X拉平成一维向量,以便与权重矩阵W进行点积运算。如果你的输入是一批二维图片,这一步会将每张图片转换为一维向量形式,使得它们能够通过全连接层。
-
nd.pick()
用于从一个多维数组中根据指定的索引挑选元素
举例:import mxnet as mx data = mx.nd.array([[1, 2], [3, 4]]) index = mx.nd.array([1, 0]) picked_elements = mx.nd.pick(data, index, axis=1)print(picked_elements.asnumpy())
在这个例子中,data 是一个二维 NDArray,而 index 是一个一维数组,指示了在 axis=1(行)上希望选取的元素索引。
nd.pick 函数会根据给定的索引数组 index,从 data 的指定维度(通过 axis 参数指定)中挑选出相应的元素。具体来说:mx.nd.array创建了两个行向量,用[1, 0]和axis=1索引的是 第0行中的第1个 和 第1行中的第0个 元素
-
accuracy(y_hat, y)
- y_hat.argmax(axis=1)
使用 argmax 函数找到 y_hat 中每一行(即每个样本)的最大值对应的索引。 - (y_hat.argmax(axis=1) == y.astype(‘float32’)):
进行逐元素比较,判断预测的类别是否等于实际类别,返回bool类型 - .mean():
计算上述布尔数组的平均值。由于 True 在数值计算中被视为 1,False 视为 0,因此平均值实际上就是预测正确的样本比例,即准确率。 - .asscalar():
将结果从 NDArray 转换为 Python 标量(如 float)
- y_hat.argmax(axis=1)