Tensor
Tensor的索引和切片
Tensor支持基本的索引和切片操作,不仅如此,它还支持ndarray中的高级索引(整数索引和布尔索引)操作。
torch.nonzero:用于返回非零的索引矩阵。
torch.where(condition, x, y): 判断condition的条件是否满足,当某个元素满足,则返回对应矩阵x相同位置的元素,否则返回矩阵y的元素。
Tensor的变换、拼接和拆分
PyTorch提供了大量的对Tensor进行操作的函数或方法,这些函数内部使用指针实现对矩阵的形状变换,拼接,拆分等操作,使得我们无须关心Tensor在内存的物理结构或者管理指针就可以方便且快速的执行这些操作。
Tensor.nelement(),Tensor.ndimension(),ndimension.size()可分别用来查看矩阵元素的个数,轴的个数(维度数)以及维度,属性Tensor.shape也可以用来查看Tensor的维度。
在PyTorch中,Tensor.reshape和Tensor.view都能被用来更改Tensor的维度。
它们的区别在于,Tensor.view要求Tensor的物理存储必须是连续的,否则将报错,而Tensor.reshape则没有这种要求。
但是,Tensor.view返回的一定是一个索引,更改返回值,则原始值同样被更改,Tensor.reshape返回的是引用还是拷贝是不确定的。
它们的相同之处都接收要输出的维度作为参数,切输出的矩阵元素个数不能改变,可以在维度中输入-1,PyTorch会自动推断它的数值。
torch.squeeze和torch.unsqueeze用来给Tensor去掉和添加轴(维度)。 torch.squeeze去掉维度为1的轴 torch.unsqueeze用于给Tensor的指定位置添加一个维度为1的轴。
torch.t和torch.transpose用于转置二维矩阵。这两个函数只接收二维Tensor,torch.t是torch.transpose的简化版。
转置矩阵
将矩阵的行列互换得到的新矩阵称为转置矩阵,转置矩阵的行列式不变。
对于高维度Tensor,可以使用permute方法来变换维度。
PyTorch提供了torch.cat和torch.stack用于拼接矩阵。
torch.cat(tensors, dim): tensors里不同张量对应的待拼接维度的size可以不一致,但是其他维度的size要保持一致。
torch.stack(tensors, dim):tensors里所有张量的维度要保持一致,否则会报错
区别: torch.cat会在dim的维度上进行合并,不会扩展出新的维度。
torch.stack则会在dim的维度上拓展出一个新的维度,然后进行拼接,该维度的大小为tensors的个数。
torch.split和torch.chunk用于拆分矩阵。 torch.chunk(input, chunks, dim):
chunks是均匀分割的份数,如果在进行分割的维度上的size不能被chunks整除,则最后一份tensor会略小(也可能为空)。
torch.split(input, split_size_or_sections, dim=0): torch.split()可以说是torch.chunk()的升级版,它不仅可以按份数均匀分割,还可以按特定的方案进行分割。 如果第二个参数是分割份数,这就和torch.chunk()一样了;
第二种是分割方案,是一个list类型的数据,待分割的张量将会被分割为len(list)份,每一份的大小取决于list中的元素。
import torch
a = torch.arange(9).view(3, 3)
# 基本索引
print(a[2, 2])
# 切片
print(a[1:, :-1])
# 带步长的切片
print(a[::2])
print(a[::2, ::2])
# 整数索引
rows = [0, 1]
cols = [2, 2]
print(a[rows, cols])
# 布尔索引
index = a > 4
print(index)
print(a[index])
a = torch.arange(9).view(3, 3)
index = torch.nonzero(a >= 8)
print(index)
a = torch.randint(0, 2, (3, 3))
print(a)
index = torch.nonzero(a)
print(index)
x = torch.randn(3, 2)
y = torch.ones(3, 2)
print(x)
print(torch.where(x > 0, x, y))
a = torch.rand(1, 2, 3, 4, 5)
print("元素个数", a.nelement())
print("轴的个数", a.ndimension())
print("矩阵维度", a.size(), a.shape)
b = a.view(2 * 3, 4 * 5)
print(b.shape)
c = a.reshape(-1)
print(c.shape)
d = a.reshape(2 * 3, -1)
print(d.shape)
b = torch.squeeze(a)
print(b.shape)
print(torch.unsqueeze(b, 0).shape)
a = torch.tensor([[2]])
b = torch.tensor([[2, 3]])
print(torch.transpose(a, 1, 0))
print(torch.t(a))
print(torch.transpose(b, 1, 0))
print(torch.t(b))
a = torch.rand((1, 224, 224, 3))
print(a.shape)
b = a.permute(0, 3, 1, 2)
print(b.shape)
a = torch.rand(2, 3)
b = torch.rand(3, 3)
# 默认维度为dim=0
c = torch.cat((a, b))
d = torch.cat((b, b, b), dim=1)
print(c.shape)
print(d.shape)
c = torch.stack((b, b), dim=1)
d = torch.stack((b, b), dim=0)
print(c.shape)
print(d.shape)
a = torch.randn(10, 3)
for x in torch.split(a, [1, 2, 3, 4], dim=0):
print(x.shape)
for x in torch.split(a, 4, dim=0):
print(x.shape)
for x in torch.chunk(a, 4, dim=0):
print(x.shape)
梯度下降
对于一个学习系统来说,我们需要找到最适合数据的模型,模型有很多,
需要不断尝试,其中最简单的一个模型就是线性模型。
我们需要去找到一个
w
的取值,使得 (
y ^
−
y
)
2
最小。
•
y=w
∗
x
可以采用穷举法求最优值
w
梯度下降算法
需要去计算每个点的
梯度
。
•
梯度
即微分(导数)
•
>0:
函数上升,损失值在增大,
w
应该减小(梯度的反方向运动);
•
<0
:函数在下降,损失值在减小(目标方向),
w
应该增大(梯度的反方向运动)。
•
所以
参数
w
的更新方向应该是梯度的负方向!
在算法开始时会给参数
w
取一个初 始值 ,然后不断更新,想要找到一个全局
最优值,使得
cost最小。
훼:学习率。决定你每一步更新走多大步。一般取值很小:0.1、0.01。
注意:梯度下降算法是一种贪心算法,得到的解不一定是全局最优。 解决:
运行多次,随机化初始点。(SGD:随机梯度下降)梯度下降法的初始点也是一个超参数。
鞍点:梯度为0的点。会导致梯度无法继续更新。
import numpy as np
import matplotlib.pyplot as plt
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
def forward(x):
return x * w
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
w_list = []
mse_list = []
for w in np.arange(0.0, 4.1, 0.1):
print("w=", w)
l_sum = 0
for x_val, y_val in zip(x_data, y_data):
y_pred_val = forward(x_val)
loss_val = loss(x_val, y_val)
l_sum += loss_val
print("\t", x_val, y_val, y_pred_val, loss_val)
print('MSE=', l_sum / 3)
w_list.append(w)
mse_list.append(l_sum / 3)
plt.plot(w_list, mse_list)
plt.ylabel('Loss')
plt.xlabel('w')
plt.show()
yn=w∗xn
cost(w)=:
损失曲线图
一般来说,用下图这种epoch、cost(loss)的损失值变化曲线来表示训练情况。
# 导入matplotlib.pyplot库,用于绘制损失函数随迭代次数的变化曲线。
import matplotlib.pyplot as plt
# 定义数据点x_data和y_data,以及初始权重w。
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w = 1.0
# 定义前向传播函数forward(x),用于计算线性模型的预测值。
def forward(x):
return x * w
# 定义损失函数cost(xs, ys),用于计算预测值与实际值之间的平方误差之和。
def cost(xs, ys):
cost = 0
for x, y in zip(xs, ys):
y_pred = forward(x)
cost += (y - forward(x)) ** 2
return cost / len(xs)
# 定义梯度函数gradient(xs, ys),用于计算损失函数关于权重w的梯度。
def gradient(xs, ys):
grad = 0
for x, y in zip(xs, ys):
grad += 2 * x * (x * w - y)
return grad / len(xs)
# 初始化两个列表epoch_list和cost_list,用于存储每次迭代的迭代次数和损失值。
epoch_list = []
cost_list = []
# 打印训练前的预测值。
print('predict(beforo training)', 4, forward(4))
for epoch in range(100):
cost_val = cost(x_data, y_data) # 计算当前权重w对预测结果的影响,即损失值(cost)。
grad_val = gradient(x_data, y_data) # 计算损失函数关于权重w的梯度。
w -= 0.01 * grad_val # 0.01是学习率,控制每次迭代中权重更新的速度。
print("epoch", epoch, 'w=', w, 'loss', cost_val) # 打印当前迭代次数、权重w和损失值。
epoch_list.append(epoch)
cost_list.append(cost_val)
print('w=', w)
print('predict(beforo training)', 4, forward(4))
# 画图
plt.plot(epoch_list, cost_list)
plt.ylabel('cost')
plt.xlabel('epoch')
plt.show()
在20 epoch以前,模型快速收敛,后面趋于稳定,损失值接近0.这是理想的训练情况。损失值随着训练越来越小,逐渐收敛,这次训练就是成功的。如果损失值随着训练还逐渐增大了,那么训练就失败了!
随机梯度下降(Stochastic Gradient Descent) 梯度下降算法:用所有样本的平均损失值cost来更新参数; 随机梯度下降算法:随机选取N个样本中的一个样本的loss来更新参数!
随机梯度下降算法能够更好的解决鞍点问题,因为是随机选取一个样本的loss,可能会跨过鞍点继续更新。
import matplotlib.pyplot as plt # plt是常用的绘制图像的库
# 训练集数据
x_data = [1.0, 2.0, 3.0] # 输入特征
y_data = [2.0, 4.0, 6.0] # 对应的目标值
w = 1.0 # 初始化权重w为1.0
# 定义线性模型y=wx
def forward(x):
return x * w
# 定义损失函数:loss=(y_predict-y)2=(x*w-y)2
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
# 计算梯度SGD
def gradient(x, y):
return 2 * x * (x * w - y)
# 创建两个空列表,分别用于存储每个训练周期
epoch_list = []
loss_list = []
# 打印预测结果
print("predict(after training", 4, forward(4))
for epoch in range(100): # 循环
for x, y in zip(x_data, y_data): # 内层循环,遍历所有的训练数据对 (x, y)
grad = gradient(x, y) # 计算当前训练数据对 (x, y) 的梯度。
w = w - 0.01 * grad
print("\tgrad:", x, y, grad)
l = loss(x, y) # 计算当前训练数据对 (x, y) 的损失值。
print("epoch", epoch, "w=", w, "loss=", l)
epoch_list.append(epoch) # 将当前训练周期的编号添加到 epoch_list 列表中。
loss_list.append(l) # 将当前训练周期的损失值添加到 loss_list 列表中。
# 画图
print("predict(after training", 4, forward(4))
plt.plot(epoch_list, loss_list)
plt.ylabel('Loss') # 设置纵坐标轴
plt.xlabel('epoch') # 设置横轴
plt.show()
比较梯度下降 随机梯度下降 整个训练集的cost计算梯度 一个样本的loss计算梯度 性能低 性能高 时间复杂度低 时间复杂度高 有一种折中的方法,性能高,时间复杂度又低采用Batch(Mini-Batch)的方法进行训练。
•
这里的线性模型是
y=wx
,如果换成
y=wx+b
,上述计算过程会变吗?会
影响参数更新吗?
请写出对应的代码。
• x_data = [1.0,2.0,3.0]
• y_data = [4.5,7.5,10.5]
import numpy as np
import matplotlib.pyplot as plt
# 数据
x_data = np.array([1.0, 2.0, 3.0])
y_data = np.array([4.5, 7.5, 10.5])
# 参数设置
learning_rate = 0.01
max_iter = 1000
# 初始化权重和偏置
w = 0.0
b = 0.0
# 存储损失值
losses = []
# 梯度下降算法
for epoch in range(max_iter):
# 计算预测值
y_pred = w * x_data + b
# 计算损失值
loss = np.mean((y_pred - y_data) ** 2) / len(x_data)
losses.append(loss)
#
# 为了最小化损失函数,我们需要计算损失函数对参数w和b的偏导数,即梯度dw和db。根据链式法则,我们有:
# dw = (∂L/ ∂w) = (∂L/ ∂y_pred) * (∂y_pred/ ∂w)
# db = (∂L/ ∂b) = (∂L/ ∂y_pred) * (∂y_pred/ ∂b)
# 由于y_pred = w * x_data + b,我们可以计算出:
# ∂y_pred /∂w = x_data
# ∂y_pred /∂b = 1
# 将这些代入上面的偏导数公式中,我们得到:
# dw = (∂L/ ∂y_pred) * x_data = (2 / n) * Σ(y_pred - y_data) * x_data
# db = (∂L/ ∂y_pred) * 1 = (2 / n) * Σ(y_pred - y_data)
#
# dw = (2 / n) * Σ(y_pred - y_data) * x_data
# db = (2 / n) * Σ(y_pred - y_data)
# 计算梯度
dw = np.mean((y_pred - y_data) * x_data)
db = np.mean(y_pred - y_data)
# 更新权重和偏置
w -= learning_rate * dw
b -= learning_rate * db
# 绘制损失值与epoch遍历次数的关系图
plt.plot(range(max_iter), losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
逻辑回归(Logistic Regression)
回顾:Linear Regression
线性回归是用于预测连续值,做预测;而逻辑回归是预测离散值,即是用来分类的。
分类任务
在机器学习领域,分类任务是监督学习中的一个重要分支,它涉及到将输入数据映射到预定义的类别标签。以下是对不同类型分类任务的概述:
二分类:这是最简单的分类任务,涉及将数据集分为两个类别,如垃圾邮件检测中的“垃圾”与“非垃圾”。多类别分类:当有两个以上的类别需要预测时,这种任务称为多类别分类,例如图像识别中的区分不同的物体类别。多标签分类:在某些情况下,一个实例可能同时属于多个类别,这类问题被称为多标签分类,如一篇文章可以同时被标记为“科技”和“新闻”。不平衡分类:在现实世界的数据集中,类别分布往往是不平衡的,即某个类别的样本数量远多于其他类别,这对分类模型的学习提出了挑战。分类方法:为了解决这些分类任务,研究者和工程师们开发了多种算法,包括决策树、支持向量机、神经网络等。每种方法都有其适用的场景和优势。评估指标:为了衡量分类模型的性能,通常会使用准确率、精确率、召回率、F1分数等指标。这些指标有助于了解模型在不同方面的性能,如正确分类的比例、误报的情况等。实际应用:分类任务广泛应用于各个领域,包括但不限于金融欺诈检测、医疗诊断、文本分类、情绪分析等。每个应用都需要根据其特定问题选择合适的算法和模型。深度学习:随着深度学习的发展,深度神经网络在处理复杂的分类任务,如图像和语音识别方面表现出色。深度学习模型能够自动提取高级特征,从而提高分类的准确性。模型选择和评估:在选择分类模型时,需要考虑问题的性质、数据的复杂性以及计算资源。一旦选择了合适的模型,就需要通过交叉验证等方法来评估其性能,确保模型在未知数据上的泛化能力。持续优化:分类模型通常需要根据新收集的数据不断进行调整和优化,以适应数据分布的变化和新的分类需求。伦理和公平性:在使用分类模型时,还需要考虑模型可能带来的偏见和不公平性问题,确保模型的决策过程是透明和公正的。
回归VS分类 在之前的回归任务中,我们是预测分值是多少,在分类任务中就可以变成根据学习时间判断是否能通过考试,即结果分为两类:fail、pass。我们的任务就是计算不同学习时间x分别是fail、pass的概率。(二分类问题其实只需要计算一个概率;另一个概况就是1-算的概率) 如果预测pass概率为0.6,fail概率就是0.4,那么判断为pass。 在分类问题中,模型输出的就是输入属于哪一个类别的概率! 概率取值[0,1],预测值y_hat不一定在这个取值区间。因此我们需要把得到的预测值y_hat通过激活函数隐射为[0,1]区间。
逻辑回归模型 只是在线性回归之后加了一个sigmoid激活函数!将值映射在【0,1】之间。 损失函数 MSE loss:计算数值之间的差异 BCE loss:计算分布之间的差异
分析:如果y=1,1-y=0,loss=-log y_hat,需要loss尽可能小,那么y_hat就要尽可能大,即尽可能接近1. 如果y=0,1-y=1,loss=-log(1-y_hat),需要loss尽可能小,那么y_hat值越接近0越好。(log 1=0) 对数函数图:
Mini-Batch loss:BCE loss 求均值
# 导入所需要的库
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
# 定义数据集x_data和y_data
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[0], [0], [1]])
# 定义一个名为LogisticRegressionModel的类
class LogisticRegressionModel(torch.nn.Module): # 继承自torch.nn.Module
#
def __init__(self):
super(LogisticRegressionModel, self).__init__()
self.linear = torch.nn.Linear(1, 1)
# 定义了一个线性层self.linear,并在forward方法中使用sigmoid函数将线性层的输出转换为概率值。
def forward(self, x):
y_pred = F.sigmoid(self.linear(x)) # 多了一个sigmid函数
return y_pred
# 接下来创建一个实例模型
model = LogisticRegressionModel()
# 定义损失函数
criterion = torch.nn.BCELoss(size_average=False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
for epoch in range(1000): # 进行循环
y_pred = model(x_data) # 对输入数据x_data进行预测,得到预测结果y_pred
loss = criterion(y_pred, y_data)
print(epoch, loss.item()) # 输出当前的迭代次数(epoch)和对应的损失值 #loss.item()张量转化为标量
optimizer.zero_grad() # 将优化器的梯度缓存清零,以便在下一次迭代时重新计算梯度
loss.backward() # 执行反向传播算法,根据损失函数计算出每个参数的梯度
optimizer.step() # 计算出的梯度更新模型的参数
x = np.linspace(0, 10, 200) # 生成一个包含200个元素的等差数列,范围从0到10
x_t = torch.Tensor(x).view((200, 1)) # 将转换为tensor,变成200行,1列
y_t = model(x_t) # 将x_t作为输入传递给模型,得到输出y_t
y = y_t.data.numpy() # tensor转化为numpy形式
# 绘制图形
plt.plot(x, y)
plt.plot([0, 10], [0.5, 0.5], c='r') # 在概率=0.5时画一条红色直线
plt.xlabel('Hours') # 设置横坐标轴的标签为“Hours”
plt.ylabel('Probability of Pass')
plt.grid() # 添加网络线
plt.show()
用PyTorch实现Logistic回归
import matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn
from torch.distributions import MultivariateNormal
from torch import optim
#设置两张不同的均值向量和协方差居住
mu1 = -3 * torch.ones(2)
mu2 = 3 * torch.ones(2)
sigma1 = torch.eye(2) * 0.5
sigma2 = torch.eye(2) * 2
#各从两个多远高斯分布中生成100个样本
m1 = MultivariateNormal(mu1, sigma1)
m2 = MultivariateNormal(mu2, sigma2)
x1 = m1.sample((100,))
x2 = m2.sample((100,))
#设置正负样本的标签
y = torch.zeros((200,1))
y[100:] = 1
#组合、打乱样本
x = torch.cat([x1,x2],dim=0)
idx = np.random.permutation(len(x))
x = x[idx]
y = y[idx]
#绘制样本
plt.scatter(x1.numpy()[:,0],x1.numpy()[:,1])
plt.scatter(x2.numpy()[:,0],x2.numpy()[:,1])
plt.show()
#线性方程
D_in,D_out = 2,1
linear = nn.Linear(D_in,D_out,bias=True)
output = linear(x)
print(x.shape,linear.weight.shape,linear.bias.shape,output.shape)
def my_linear(x,w,b):
return torch.mm(x,w.t())+b
print(torch.sum((output - my_linear(x,linear.weight,linear.bias))))
#激活函数
sigmoid = nn.Sigmoid()
scores = sigmoid(output)
def my_sigmoid(x):
x = 1 / (1 + torch.exp(-x))
return x
print(torch.sum(sigmoid(output) - my_sigmoid(output)))
loss = nn.BCELoss()
loss(sigmoid(output),y)
def my_loss(x,y):
loss = - torch.mean(torch.log(x) * y + torch.log(1 - x)* (1 - y))
return loss
print(loss(sigmoid(output),y) - my_loss(my_sigmoid(output),y))
class LogisticRegression(nn.Module):
def __init__(self, D_in):
super(LogisticRegression,self).__init__()
self.linear = nn.Linear(D_in,1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.linear(x)
output = self.sigmoid(x)
return output
lr_model = LogisticRegression(2)
loss = nn.BCELoss()
print(loss(lr_model(x), y))
class MyModel(nn.Module):
def __init__(self):
super(MyModel,self).__init__()
self.linear1= nn.Linear(1,1,bias=False)
self.linear2= nn.Linear(1,1,bias=False)
def forward(self):
pass
for param in MyModel().parameters():
print(param)
#优化算法
optimizer = optim.SGD(lr_model.parameters(),lr=0.03)
batch_size = 10
iters = 10
for i in range(iters):
for j in range(int(len(x)%batch_size)):
input = x[i*batch_size:(j+1)*batch_size]
target = y[i*batch_size:(j+1)*batch_size]
optimizer.zero_grad()
output = lr_model(input)
l = loss(output,target)
l.backward()
optimizer.step()
#测试
output = lr_model(x)
# 计算模型精确率并输出
correct = (lr_model(x).round() == y).float().sum()
accuracy = correct / len(y)
print("模型精确率为:", accuracy.item())
#模型可视化
pred_neg = (output<=0.5).view(-1)
pred_pos = (output>0.5).view(-1)
plt.scatter(x[pred_neg,0],x[pred_neg,1])
plt.scatter(x[pred_pos,0],x[pred_pos,1])
w = lr_model.linear.weight[0]
b = lr_model.linear.bias[0]
def draw_decision_boundary(w,b,x0):
x1 = (-b - w[0] * x0) / w[1]
plt.plot(x0.detach().numpy(),x1.detach().numpy(),'r')
plt.show()
draw_decision_boundary(w,b,torch.linspace(x.min(),x.max(),50))
文章来源
发表评论