本篇笔记的完整代码:https://github.com/ChenWentai/PyTorch/blob/master/task3_logistic.py
1. 准备数据
这次任务使用Logistic解决二分类问题。对于Logistic回归,数据的标签为0和1(而不是1和-1),其中y=0的训练数据由均值为2,方差为1正态分布产生,y=1的训练数据由均值为-2, 方差为1的正态分布产生。
此处数据参考Liam Coder的博客https://blog.csdn.net/out_of_memory_error/article/details/81275651
import torch
from torch.autograd import Variable
N = torch.ones(100, 2) #训练样本数
x0 = Variable(torch.normal(2*N, 1))
y0 = Variable(torch.zeros(100, 1))
x1 = Variable(torch.normal(-2*N, 1))
y1 = Variable(torch.ones(100, 1))
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)
y = torch.cat((y0, y1), 0).type(torch.FloatTensor)
#作出散点图
fig, ax = plt.subplots()
labels = ['class 0','class 1']
ax.scatter(x.numpy()[0:len(x0),0], x.numpy()[0:len(x0),1], label=labels[0])
ax.scatter(x.numpy()[len(x0):len(x),0], x.numpy()[len(x0):len(x),1], label=labels[1])
ax.legend()
数据分布如下:
2. 使用Pytorch Tensor实现Logistic回归
Logistic回归采用最大似然法求解参数的最优值。 似然函数如下:
其中 表示有N个样本, 是Logistic函数。通过梯度下降法可以求得参数 的最优值。注意,此处的 包含了偏置 .
(1)梯度下降求解参数 和
#初始化w和b
w = Variable(torch.zeros(2, 1), requires_grad = True)
b = Variable(torch.zeros(1, 1), requires_grad = True)
EPOCHS = 200
likelihood = []
lr = 0.01
for epoch in range(EPOCHS):
A = 1/(1+torch.exp(-(x.mm(w)+b))) #Logistic函数
J = -torch.mean(y*torch.log(A) + (1-y)*torch.log(1-A)) #对数似然函数
likelihood.append(-J.data.numpy().item())
J.backward() #求似然函数对w和b的梯度
w.data = w.data - lr * w.grad.data #更新w
w.grad.data.zero_()
b.data = b.data - lr * b.grad.data #更新b
b.grad.data.zero_()
(2)作出似然函数的图像:
#
import matplotlib.pyplot as plt
plt.plot(likelihood)
plt.ylabel("lieklihood")
plt.xlabel("epoch")
plt.show()
P.S. 这里似然函数的公式为J = -torch.mean(y*torch.log(A) + (1-y)*torch.log(1-A))
, 由前述的求和项改为了平均值。个人观点是为了适应PyTorch的求导规则。如果使用torch.sum()
,在梯度下降的过程中会出现似然函数为nan
的现象。具体原因有待进一步探究。
(3) 作出分类边界图像:
xa = list(range(-4, 5))
xb = []
for item in xa:
xb.append(-(b.data + item*w[0])/w[1])
fig, ax = plt.subplots()
labels = ['class 0','class 1']
ax.scatter(x.numpy()[0:len(x0),0], x.numpy()[0:len(x0),1], label=labels[0])
ax.scatter(x.numpy()[len(x0):len(x),0], x.numpy()[len(x0):len(x),1], label=labels[1])
ax.legend()
plt.plot(xa, xb)
plt.show()
3. 使用nn.Module实现Logistic回归
(1)搭建nn模型,梯度下降求解参数和
from torch import nn
class Logistic(nn.Module):
def __init__(self):
super(Logistic, self).__init__()
self.linear = nn.Linear(2,1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
y_pred = self.linear(x)
y_pred = self.sigmoid(y_pred)
return y_pred
model = Logistic()
criterion = nn.BCELoss()
optimizer = torch.optim.SGD(model.parameters(), lr= 0.001)
EPOCHS = 1000
costs = []
for epoch in range(EPOCHS):
x = Variable(x)
y = Variable(y)
out = model(x)
loss = criterion(out, y)
costs.append(loss.data.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
(2)作出损失函数的图像:
#
import matplotlib.pyplot as plt
plt.plot(costs)
plt.show(range(len(costs)), costs)
(3) 作出分类边界图像:
w1, w2 = model.linear.weight[0]
b = model.linear.bias.item()
plot_x = range(-5, 6, 1)
plot_y = [-(w1*item+b)/w2 for item in plot_x]
fig, ax = plt.subplots()
labels = ['class 0','class 1']
ax.scatter(x.numpy()[0:len(x0),0], x.numpy()[0:len(x0),1], label=labels[0])
ax.scatter(x.numpy()[len(x0):len(x),0], x.numpy()[len(x0):len(x),1], label=labels[1])
ax.legend()
ax.plot(plot_x, plot_y)