第六讲 logistic回归
逻辑回归解决的是分类问题
手写数字问题
算出对应每个数字的概率值,选取最大的即为输出数字
计算属于每个分类的概率\
分类问题
- sigmoid函数: 将区间映射到0-1之间,映射为概率
1 1 + e − x \frac{1}{1+e^{-x}} 1+e−x1
- 其他的常用激活函数
逻辑回归模型的计算图
损失函数
线性回归中的损失函数使用预测减真实的平方
l
o
s
s
=
(
y
^
−
y
)
2
=
(
x
∗
w
−
y
)
loss = (\widehat{y}-y)^2=(x*w-y)
loss=(y
−y)2=(x∗w−y)
二分类问题损失函数使用交叉熵形式(
y
^
\widehat{y}
y
是y=1时概率,1-
y
^
\widehat{y}
y
是y=0时概率):
l
o
s
s
=
−
(
y
l
o
g
y
^
+
(
1
−
y
)
l
o
g
(
1
−
y
^
)
)
loss =- (ylog\widehat{y}+(1-y)log(1-\widehat{y}))
loss=−(ylogy
+(1−y)log(1−y
))
Mini-Batch时损失函数:
l
o
s
s
=
−
1
N
∑
1
n
(
y
n
l
o
g
)
y
^
+
(
1
−
y
n
)
l
o
g
(
1
−
y
n
^
)
loss = -\frac{1}{N}\sum_1^n(y_nlog)\widehat{y}+(1-y_n)log(1-\widehat{y_n})
loss=−N11∑n(ynlog)y
+(1−yn)log(1−yn
)
逻辑回归模型实现:
逻辑回归实现代码
import torchvision
import torch.nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
x_data = torch.Tensor([[1.0], [2.0], [3.0]]) #输入x
y_data = torch.Tensor([[0], [0], [1]]) #输出y
class LogisticRegressionModel(torch.nn.Module):
def __init__(self):
super(LogisticRegressionModel, self).__init__()
self.linear = torch.nn.Linear(1,1) #输入输出维度为(1,1)
def forward(self, x):
y_pred = F.sigmoid(self.linear(x))
return y_pred
model = LogisticRegressionModel()
#损失及优化器
#BCE损失函数
criterion = torch.nn.BCELoss(size_average=False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
#进行循环训练
for epoch in range(100):
y_pred = model(x_data)
loss = criterion(y_pred, y_data)
print(epoch, loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('w=', model.linear.weight.item())
print('b=', model.linear.bias.item())
# 测试
x_test = torch.Tensor([4.0])
y_test = model(x_test)
print('y_pred=', y_test.data)
# 每周学习时间,0-10个小时,采集200个点
x = np.linspace(0, 10,200)
x_t = torch.Tensor(x).view((200,1))
y_t = model(x_t)
y = y_t.data.numpy()
plt.plot(x,y)
plt.plot([0,10],[0.5, 0.5], c='r')
plt.xlabel('hours')
plt.ylabel('probability of pass')
plt.grid()
plt.show()
- 和线性回归相比:
- 增加了一个sigmoid函数
- 损失函数由MSE变为BCE
第七讲 多维空间输入
输入可以有多个维度
逻辑回归模型
- 对于多个维度的输入,其逻辑回归模型表达式也相应变化,本来是关于一个维度的线性函数+sigmoid,现在变为关于这若干个维度的线性函数+sigmoid
Mini-Batch(N个样本) - 参数w不受矩阵影响,样本数为N,参数为8,输入维度为8维,输出维度是1维,linear(8,1)
- 目标是找到从8维空间到一维空间的非线性变换
- 每一次空间压缩都会引入非线性,即可以从8维降维到6维,6维后可继续降维至一维空间,找到8维空间到一位空间的非线性神经网络,添加sigma拟合非线性变换
- 因此我们可以构造多个线性层连接,每层之后用函数激活,中间维度具有承接的关系。
模型实现四部曲
- 准备数据集
每一行为一个样本,前8列为输入的8个特征维度,最后一列为标签值
- 定义模型
根据计算图转换代码
- 损失函数和优化器
和之间没有太大区别,此处loss取了平均值
- 周期性训练
没有太大差别,前向、后向、更新。
实现代码
import torchvision
import torch
import torch.nn.functional as F
import numpy as np
xy = np.loadtxt('diabetes.csv.gz', delimiter=',', dtype=np.float32) # 分隔符,以逗号进行分割
x_data = torch.from_numpy(xy[:,:-1]) #所有行,最后一列不要
# [-1]加中括号拿出来是矩阵,不加是向量
y_data = torch.from_numpy(xy[:, [-1]]) #所有行,只要倒数最后一列
#定义模型,从8维-6维-4维-1维输出,使用sigmoid激活
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.linear1 = torch.nn.Linear(8,6)
self.linear2 = torch.nn.Linear(6,4)
self.linear3 = torch.nn.Linear(4,1)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
x = self.sigmoid(self.linear1(x))
x = self.sigmoid(self.linear2(x))
x = self.sigmoid(self.linear3(x)) #输出即为y^
return x
model = Model()
#损失及优化器
criterion = torch.nn.BCELoss(size_average=True)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
#进行循环训练
for epoch in range(1000):
#forward
y_pred = model(x_data)
loss = criterion(y_pred, y_data)
print(epoch, loss.item())
#backward
optimizer.zero_grad()
loss.backward()
#update
optimizer.step()
第八讲 数据集和数据加载
名词解释
- Epoch:一个Epoch会将所有样本进行一次正向传播和反向传播
- Batch-size:一次前向传播和反向传播的训练样本个数
- iteration:内层迭代的次数,每次迭代使用batch size个样本
- 如果有10000个样本,batch size = 1000,则iteration=10
- shuffle:将数据集打乱
Dataloader功能:(batchsize= 2, shuffle=True)
- 先打乱,然后进行分组
定义自己的数据集
- 需要实现的魔法方法,比如__getitem__,__len__自定义的数据集继承自抽象类 Dataset(位于 torch.utils.data 中)
实现代码
import torch
import numpy as np
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
class DiabetesDataset(Dataset): #继承自dataset父类的一些基本功能
def __init__(self, filepath):
xy = np.loadtxt(filepath, delimiter=',',dtype=np.float32)
self.len = xy.shape[0] # 根据shape[0]的值确定数据集个数
self.x_data = torch.from_numpy(xy[:, :-1])
self.y_data = torch.from_numpy(xy[:, [-1]])
def __getitem__(self, index):
return self.x_data[index], self.y_data[index]
def __len__(self):
return self.len
dataset = DiabetesDataset('diabetes.csv.gz')
train_loader = DataLoader(dataset=dataset, #传递数据集
batch_size=32, #一个小批量的容量是多少
shuffle=True, #是否打乱顺序
num_workers=2) #读数据时是否多线程,是否并行
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
self.linear1 = torch.nn.Linear(8,6)
self.linear2 = torch.nn.Linear(6,4)
self.linear3 = torch.nn.Linear(4,1)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
x = self.sigmoid(self.linear1(x))
x = self.sigmoid(self.linear2(x))
x = self.sigmoid(self.linear3(x)) #输出即为y^
return x
model = Model()
#损失及优化器
criterion = torch.nn.BCELoss(size_average=True)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
if __name__ == '__main__':
for epoch in range(100):
for i, data in enumerate(train_loader, 0): #构造(x,y)
#1. 准备数据
inputs, labels = data
#2. 前馈
y_pred = model(inputs)
loss = criterion(y_pred, labels)
print(epoch, i, loss.item())
#3. 反馈
optimizer.zero_grad()
loss.backward()
#4. 更新
optimizer.step()
第九讲 Softmax
多分类问题
- 对于多分类问题,之前二分类问题方法不适用,我们不希望仅仅输出一个 0 到 1 之间的数值作为概率预测,我们希望能够输出一个概率的分布。
Softmax函数
设
z
l
ϵ
R
k
z^l\epsilon R^k
zlϵRk是最后一层线性层的输出,我们定义Softmax函数:
- 这个函数符合预期,每个概率值为非负,各个概率值之和为1
损失函数
- 仍然使用交叉熵进行损失函数的运算,预测标签是一个独热矩阵,取对应标签的预测值的负log值乘以原始预测标签,作为损失值
- 从最后一层到得到loss值的计算过程: