Softmax回归虽然叫做回归,但其解决的是分类问题
回归VS分类
- 回归估计一个连续值,输出往往只有一个
- 分类预测一个离散类别,通常有多个输出,输出
i
i
i是预测为第
i
i
i类的置信度
从回归到多类分类-校验比例
-
对类别进行一位有效编码
y = [ y 1 , y 2 , . . . , y n ] T y i = θ p ( x ) = { 1 i f i = y 0 o t h e r w i s e begin{aligned}y&=[y_1,y_2,...,y_n]^T \y_i&=theta_p(x)=left{ begin{array}{rcl} 1 & & {ifquad i=y}\ 0 & & {otherwise} end{array} right.end{aligned} yyi=[y1,y2,...,yn]T=θp(x)={10ifi=yotherwise -
最大值最为预测
y ^ = arg max i o i hat y=argmax_io_i y^=iargmaxoi
同时为了转化成概率问题,我们让该模型所有输出和为1,于是就引出了softmax函数。
y
^
i
=
e
o
i
∑
k
=
1
n
e
o
k
hat y_i=frac{e^{o_i}}{sumlimits_{k=1}^ne^{o_k}}
y^i=k=1∑neokeoi
此时每一个输出
y
^
i
hat y_i
y^i就可以看作分类为
i
i
i的概率,同样我们选择最大的概率作为其分类
softmax和交叉熵损失
交叉熵通常用来衡量两个概率的区别:
L
=
1
n
∑
i
=
1
n
(
−
∑
j
=
1
C
y
i
,
j
log
p
i
,
j
)
L=frac1nsumlimits_{i=1}^n(-sumlimits_{j=1}^Cy_{i,j}log p_{i,j})
L=n1i=1∑n(−j=1∑Cyi,jlogpi,j)
其中
n
n
n为bath_size,C为类的数目,
y
i
,
j
y_{i,j}
yi,j表示第
i
i
i个样本在第
j
j
j个标签上的真实数值(往往一个
i
i
i中只有一个值为1,其余都为0),
p
i
,
j
p_{i,j}
pi,j表示第
i
i
i个样本被预测为第
j
j
j个类别的概率
如果我们看单一样本:
L i = − ∑ j = 1 C y i , j log p i , j L_i=-sumlimits_{j=1}^Cy_{i,j}log p_{i,j} Li=−j=1∑Cyi,jlogpi,j
前面有提过所有分类中只有一个标签值为1,其余都为0,因此我们可以得到:
L
i
=
−
log
p
Y
(
i
)
L_i=-log p_{Y_{(i)}}
Li=−logpY(i)
其中
p
Y
(
i
)
p_{Y_{(i)}}
pY(i)表示被正确分类的概率,同时我们扩充到整个bath_size可以得到:
L = 1 n ∑ i = 1 n ( − log p Y ( i ) ) L=frac1nsumlimits_{i=1}^n(-log p_{Y_{(i)}}) L=n1i=1∑n(−logpY(i))
因此我们就可以使用softmax函数来求得 p Y ( i ) p_{Y_{(i)}} pY(i),我们使用交叉熵函数作为损失函数
Softmax回归从零实现
- 数据集大致介绍:此数据集一共有十个类别(不同的服装),训练集有60000张图片,测试集有10000张,
import torch import torchvision from matplotlib import pyplot as plt from torchvision import datasets from torchvision import transforms from torch.utils import data
# 获取数据集
trans = transforms.ToTensor()
train_data = datasets.FashionMNIST(root='../data/', train=True,
transform=trans, download=True)
test_data = datasets.FashionMNIST(root='../data/', train=False,
transform=trans, download=True)
print(len(train_data))
print(len(test_data))
60000 10000
train_data[0][0].shape # 原数据集的形状是 1 * 28 * 28的
torch.Size([1, 28, 28])
# 获取所有类别 classes = train_data.classes
# 可视化几张图片
def show_imges(imgs, num_rows, num_cols, scale=1.5, titles=None):
figsize = (num_cols * scale, num_rows * scale)
_, axes = plt.subplots(num_rows, num_cols, figsize=figsize)
axes = axes.flatten()
for i, (img, axe) in enumerate(zip(imgs, axes)):
if torch.is_tensor(img):
axe.imshow(img.numpy())
else:
axe.imshow(img)
axe.axes.get_xaxis().set_visible(False) # 隐藏X轴和y轴
axe.axes.get_yaxis().set_visible(False)
if titles:
axe.set_title(titles[i])
X, y = next(iter(data.DataLoader(train_data, batch_size=18))) print(X.shape) titles = [classes[i] for i in y] show_imges(X.reshape(18, 28, 28), 3, 6, titles=titles)
torch.Size([18, 1, 28, 28])
# 获取数据采集器
def get_dataloader(batch_size, train_data, test_data):
train_dataloader = data.DataLoader(train_data, batch_size=batch_size, shuffle=True)
test_dataloader = data.DataLoader(test_data, batch_size=batch_size, shuffle=False)
return train_dataloader, test_dataloader
# 定义softmax过程
def softmax(x):
x_exp = torch.exp(x)
sum_exp = torch.sum(x_exp, dim=-1, keepdim=True)
return x_exp / sum_exp
# 定义训练模型
def model(X, w, b):
return softmax(torch.matmul(X.reshape((-1, w.shape[0])), w) + b)
# 定义交叉熵损失函数
def cross_entropy(y, y_hat):
return -torch.log(y_hat[range(len(y_hat)), y])
# 定义优化方式,仍然使用MBGD
def mbgd(params, lr, batch_size):
with torch.no_grad():
for param in params:
param -= lr * param.grad / batch_size
param.grad.zero_()
# 定义评判方法,对于分类问题,我们常使用准确率来判定
def accuracy(y, y_hat):
padding = torch.argmax(y_hat, -1)
right = (padding == y).sum().numpy()
return right / y.shape[0]
# 进行训练
lr = 0.03
batch_size = 100
epochs = 10
w = torch.normal(0, 0.01, size=(28*28,10), requires_grad=True)
b = torch.zeros(10, requires_grad=True)
train_dataloader, test_dataloader = get_dataloader(batch_size, train_data, test_data)
for epoch in range(epochs):
train_acc = 0
test_acc = 0
for X,y in train_dataloader:
y_hat = model(X, w, b)
l = cross_entropy(y, y_hat)
l.sum().backward() # 进行反向传播计算梯度
mbgd([w, b], lr, batch_size)
train_acc += accuracy(y, y_hat)
# 计算在测试集上的准确率
with torch.no_grad():
for x,Y in test_dataloader:
Y_hat = model(x, w, b)
test_acc += accuracy(Y, Y_hat)
print(f'epoch is now{epoch + 1}, the accuracy on train data is {train_acc / (len(train_data) / bacth_size)}, and the accuracy on test data is {test_acc / (len(test_data) / bacth_size)}')
epoch is now1, the accuracy on train data is 0.7468833333333319, and the accuracy on test data is 0.7848999999999999 epoch is now2, the accuracy on train data is 0.8095833333333329, and the accuracy on test data is 0.8081999999999997 epoch is now3, the accuracy on train data is 0.8225000000000002, and the accuracy on test data is 0.8149 epoch is now4, the accuracy on train data is 0.8292666666666669, and the accuracy on test data is 0.8205000000000001 epoch is now5, the accuracy on train data is 0.8340500000000005, and the accuracy on test data is 0.8242999999999998 epoch is now6, the accuracy on train data is 0.8371499999999998, and the accuracy on test data is 0.8253 epoch is now7, the accuracy on train data is 0.839849999999999, and the accuracy on test data is 0.8286999999999997 epoch is now8, the accuracy on train data is 0.8423000000000006, and the accuracy on test data is 0.8280999999999996 epoch is now9, the accuracy on train data is 0.8442166666666672, and the accuracy on test data is 0.8315999999999996 epoch is now10, the accuracy on train data is 0.8458499999999998, and the accuracy on test data is 0.8337999999999997
softmax回归简洁实现
import torch import torchvision from matplotlib import pyplot as plt from torchvision import datasets from torchvision import transforms from torch.utils import data import torch.nn as nn
# 获取数据集
trans = transforms.ToTensor()
train_data = datasets.FashionMNIST(root='../data/', train=True,
transform=trans, download=True)
test_data = datasets.FashionMNIST(root='../data/', train=False,
transform=trans, download=True)
# 获取数据采集器
def get_dataloader(batch_size, train_data, test_data):
train_dataloader = data.DataLoader(train_data, batch_size=batch_size, shuffle=True, d)
test_dataloader = data.DataLoader(test_data, batch_size=batch_size, shuffle=False)
return train_dataloader, test_dataloader
# 定义训练模型 (我们直接使用torch.nn里面集成的库即可)
model = nn.Sequential(
nn.Flatten(),
nn.Linear(28*28, 10)
)
# 初始化模型参数
def init_wight(net):
if type(net) == nn.Linear:
nn.init.normal_(net.weight, std=0.01)
model.apply(init_wight)
Sequential( (0): Flatten(start_dim=1, end_dim=-1) (1): Linear(in_features=784, out_features=10, bias=True) )
# 定义损失函数 loss = nn.CrossEntropyLoss()
# 定义优化方式 optimizer = torch.optim.SGD(model.parameters(), lr=0.03)
# 定义评判方法,对于分类问题,我们常使用准确率来判定
def accuracy(y, y_hat):
padding = torch.argmax(y_hat, -1)
right = (padding == y).sum().numpy()
return right / y.shape[0]
# 进行训练
lr = 0.03
batch_size = 100
epochs = 10
train_dataloader, test_dataloader = get_dataloader(batch_size, train_data, test_data)
for epoch in range(epochs):
train_acc = 0
test_acc = 0
for X,y in train_dataloader:
y_hat = model(X)
l = loss(y_hat, y)
optimizer.zero_grad()
l.backward() # 进行反向传播计算梯度
optimizer.step()
train_acc += accuracy(y, y_hat)
# 计算在测试集上的准确率
with torch.no_grad():
for x,Y in test_dataloader:
Y_hat = model(x)
test_acc += accuracy(Y, Y_hat)
print(f'epoch is now{epoch + 1}, the accuracy on train data is {train_acc / (len(train_data) / batch_size)}, and the accuracy on test data is {test_acc / (len(test_data) / batch_size)}')
epoch is now1, the accuracy on train data is 0.8088666666666672, and the accuracy on test data is 0.8020000000000005 epoch is now2, the accuracy on train data is 0.8227500000000004, and the accuracy on test data is 0.8151 epoch is now3, the accuracy on train data is 0.8292666666666663, and the accuracy on test data is 0.8188000000000002 epoch is now4, the accuracy on train data is 0.83535, and the accuracy on test data is 0.8214999999999997 epoch is now5, the accuracy on train data is 0.8376166666666672, and the accuracy on test data is 0.8234999999999998 epoch is now6, the accuracy on train data is 0.840483333333333, and the accuracy on test data is 0.8277999999999995 epoch is now7, the accuracy on train data is 0.8423333333333328, and the accuracy on test data is 0.8299999999999998 epoch is now8, the accuracy on train data is 0.8441166666666657, and the accuracy on test data is 0.8317000000000003 epoch is now9, the accuracy on train data is 0.8461333333333325, and the accuracy on test data is 0.8334999999999997 epoch is now10, the accuracy on train data is 0.8475833333333337, and the accuracy on test data is 0.8334999999999998



