前言
- 本专栏是我这个小菜鸡跟随 B 站 up 主
刘二大人
学习pytorch
完成的课后作业,原视频请戳这里
题目
- 使用模型
y
=
w
∗
x
+
b
y=w*x+b
y=w∗x+b
计算损失
,还要使用 matplotlib 绘制 3D 图像
分析
- 首先,我们需要训练集才能进行模型训练,我们就以视频中的例子,即
train_x = [1.0, 2.0, 3.0]
train_y = [2.0, 4.0, 6.0]
- 然后,我们以
w
∈
[
0.0
,
6.0
]
w \in [0.0,\,6.0]
w∈[0.0,6.0],
b
∈
[
−
6.0
,
6.0
]
b \in [-6.0,\,6.0]
b∈[−6.0,6.0] 为例,因为有两个变量,
w
,b
,所以我们使用双层循环遍历他们的每一个组合,并计算预测值与真实值之间的均差,即loss
,使用DataFrame
存取数据,代码如下
import numpy as np
import pandas as pd
def forward(w, x, b):
return w * x + b
def process(w, x, b, y):
y_pred = forward(w, x, b)
return pow(y_pred - y, 2)
w_list = np.arange(0.0, 6.1, 0.1) # arange 左闭右开
b_list = np.arange(-6.0, 6.1, 0.1)
result = []
for w_index, w in enumerate(self.w):
for b_index, b in enumerate(self.b):
losses = pd.Series([0.0])
for x, y in zip(self.X, self.y):
losses = losses.append(pd.Series(process(w, x, b, y)))
result.loc[result.shape[0]+1] = (w, b, losses.std())
print(result)
---------------------------------
w b loss
1 -6.0 -6.0 390.969735
2 -6.0 -5.9 388.531712
3 -6.0 -5.8 386.101901
4 -6.0 -5.7 383.680285
5 -6.0 -5.6 381.266851
... ... ... ...
14637 6.0 5.6 132.347889
14638 6.0 5.7 133.795879
14639 6.0 5.8 135.252733
14640 6.0 5.9 136.718468
14641 6.0 6.0 138.193102
- 获得了数据以后,我们就要绘图了,我们使用
matplotlib
绘制3D
图像,这里我选用Axes3D
,我们看一下官方样例
- 看起来好像是需要三个
array
,其中np.meshgrid
作用是将两个一维的转换成两个二维的 - 我的尝试
x, y = np.meshgrid(result['w'], result['b']) z = result['loss'] fig = plt.figure() ax = Axes3D(fig) fig.add_axes(ax) ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.viridis) plt.show()
- 但是这样会报错
ValueError: Argument Z must be 2-dimensional.
- 意思是变量
z
必须得是个二维的数组
- 看起来好像是需要三个
bug 解决
- 既然要二维的数组,那我
result['loss'].to_frame()
一下可以吗?- 然后又报错了/(ㄒoㄒ)/~~
shape mismatch: objects cannot be broadcast to a single shape
- 意思是没有办法对应维度,也就是说,z.shape 必须和 x.shape、y.shape 一致,那这还不好办?直接 reshape
z = np.array(result['loss']).reshape(x.shape[0], -1) # -1 表示不晓得这个是多少,要根据数据量和已知维度来判断
- 绘图结果如下
- 嘶,不太对的样子,按理说不应该有这么离谱的图像,出现这种结果可能的原因是 reshape 之后,z 轴的数据与 x 和 y 轴的数据不对应了
- 最后的解决办法,
遍历
(遍历 yyds
,虽然会很慢)
z = [] for x_, y_ in zip(x, y): z_ = [] for w, b in zip(x_, y_): loss = data[(data['w'] == w) & (data['b'] == b)]['loss'].values[0] z_.append(loss) z.append(z_)
- 还有个问题是,x 和 y都是有重复数据的,所以先去重再升维度
x, y = np.meshgrid(data['w'].drop_duplicates(), data['b'].drop_duplicates())
代码展示
- 我封装了一个类,还加上了进度条,所以看着有点复杂,但还好
- 计算时以单个 x 和 y 计算
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
class train:
def __init__(self, X, y, w_limit, b_limit, span=0.1):
self.X = X
self.y = y
self.w = np.arange(w_limit[0], w_limit[1] + span, span)
self.b = np.arange(b_limit[0], b_limit[1] + span, span)
self.result = pd.DataFrame(columns=['w', 'b', 'loss'])
# 预测
def __forward(self, w, x, b):
return w * x + b
# 计算损失
def __loss(self, w, x, b, y):
y_pred = self.__forward(w, x, b)
return pow(y-y_pred, 2)
def process(self):
for w in self.w:
print('w = {:>.2f}'.format(w))
for b_index, b in enumerate(self.b):
losses = []
count = b_index + 1
for x, y in zip(self.X, self.y):
losses.append(self.__loss(w, x, b, y))
self.result.loc[self.result.shape[0]+1] = (w, b, sum(losses) / len(losses))
min_index = self.result[self.result['w'] == w].sort_values(by='loss').iloc[0]
print('\r{:>6.2f}%: [{}{}] b={:>.2f} min_loss={:>.2f}'.format(
count / len(self.b) * 100,
'■' * int(count / len(self.b) * 20),
'□' * (20 - int(count / len(self.b) * 20)),
min_index['b'], min_index['loss']
), end='')
print()
print('loss: %f' % (self.result['loss'].std()))
def get_result(self):
return self.result
train_x = [1.0, 2.0, 3.0]
train_y = [2.0, 4.0, 6.0]
model = train(train_x, train_y, (-3.0, 6.0), (-6.0, 6.0), 0.1)
model.process()
data = model.get_result()
x, y = np.meshgrid(data['w'].drop_duplicates(), data['b'].drop_duplicates())
z = []
for x_, y_ in zip(x, y):
z_ = []
for w, b in zip(x_, y_):
# 找到对应的 loss
loss = data[(data['w'] == w) & (data['b'] == b)]['loss'].values[0]
z_.append(loss)
z.append(z_)
z = np.array(z)
fig = plt.figure()
ax = Axes3D(fig)
fig.add_axes(ax)
ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.viridis)
plt.show()
- 以整个 train_x 和 train_y 计算,类 train 改为如下
class train:
def __init__(self, X, y, w_limit, b_limit, span=0.1):
self.X = np.array(X) if type(X) == list else X
self.y = np.array(y) if type(y) == list else y
self.w = np.arange(w_limit[0], w_limit[1] + span, span)
self.b = np.arange(b_limit[0], b_limit[1] + span, span)
self.result = pd.DataFrame(columns=['w', 'b', 'loss'])
def __forward(self, w, X, b):
return w * X + b
def __loss(self, y, y_pred):
return pow(y - y_pred, 2)
def process(self):
for w in self.w:
print('w = {:>.2f}'.format(w))
for b_index, b in enumerate(self.b):
count = b_index + 1
y_pred = self.__forward(w, self.X, b)
loss = self.__loss(self.y, y_pred)
self.result.loc[self.result.shape[0]+1] = (w, b, loss.sum() / loss.shape[0])
min_index = self.result[self.result['w'] == w].sort_values(by='loss').iloc[0]
print('\r{:>6.2f}%: [{}{}] b={:>.2f} min_loss={:>.2f}'.format(
count / len(self.b) * 100,
'■' * int(count / len(self.b) * 20),
'□' * (20 - int(count / len(self.b) * 20)),
min_index['b'], min_index['loss']
), end='')
print()
print('loss: %f' % (self.result['loss'].std()))
def get_result(self):
return self.result
- 运行结果(部分)如下
- 其中,紫色的那一条目测可见的线就是所有 w 和 b 对应的最小 loss
- 成功完成作业!!!
结尾
以上就是我要分享的内容,因为学识尚浅,会有不足,还请各位大佬指正。
有什么问题也可在评论区留言。