AutoCV第九课:ML基础

2023-10-11 10:30
文章标签 基础 ml 第九课 autocv

本文主要是介绍AutoCV第九课:ML基础,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • ML基础
    • 注意事项
    • 一、2023/6/28更新
    • 前言
    • 1. 矩阵乘法和求导
    • 2. 多元线性回归
    • 3. 多模型线性回归
    • 4. 多分类逻辑回归
    • 总结

ML基础

注意事项

一、2023/6/28更新

新增多元和多模型线性回归的矩阵表达,即第 2、3 小节内容
新增逻辑回归多分类 mnist 模型,即第 4 小节内容

前言

手写AI推出的全新保姆级从零手写自动驾驶CV课程,链接。记录下个人学习笔记,仅供自己参考。

本次课程主要学习矩阵运算的基础,考虑使用矩阵来表达多个线性回归模型。

课程大纲可看下面的思维导图。

在这里插入图片描述

1. 矩阵乘法和求导

先回忆下矩阵相关知识

定义矩阵乘法
{ a b d e } × { 1 3 2 4 } = { a 1 + b 2 a 3 + b 4 d 1 + e 2 d 3 + e 4 } \left\{\begin{array}{cc}a&b\\ d&e\end{array}\right\}\times\left\{\begin{array}{cc}1&3\\ 2&4\end{array}\right\}=\left\{\begin{array}{cc}a1+b2&a3+b4\\ d1+e2&d3+e4\end{array}\right\} {adbe}×{1234}={a1+b2d1+e2a3+b4d3+e4}
记法:C[r][c] = 乘加(A中取 r 行,B中取 c 列)

在这里插入图片描述

图1 矩阵乘法示例

参考:https://www.cnblogs.com/ljy-endl/p/11411665.html

矩阵求导

对于 A ⋅ B = C A\cdot B = C AB=C 定义 L L L 是关于 C C C 的损失函数

G = ∂ L ∂ C G = \dfrac{\partial L}{\partial C} G=CL 若直接 C C C A A A 求导,则 G G G 定义为 C C C 大小的全 1 矩阵,则有:
∂ L ∂ A = G ⋅ B T ∂ L ∂ B = A T ⋅ G \dfrac{\partial L}{\partial A}=G\cdot B^T \ \ \ \ \ \dfrac{\partial L}{\partial B}=A^T \cdot G AL=GBT     BL=ATG
矩阵求导推导

  1. 考虑矩阵乘法 A ⋅ B = C A \cdot B = C AB=C

  2. 考虑 Loss 函数 L = ∑ i m ∑ j n ( C i j − p ) 2 L = \sum^m_{i}\sum^n_{j}{(C_{ij} - p)^2} L=imjn(Cijp)2

  3. 考虑 C C C 的每一项导数 ▽ C i j = ∂ L ∂ C i j \triangledown C_{ij} = \frac{\partial L}{\partial C_{ij}} Cij=CijL

  4. 考虑 A B C ABC ABC 都为 2x2 矩阵时,定义 G G G L L L C C C 的导数
    A = [ a b c d ] B = [ e f g h ] C = [ i j k l ] G = ∂ L ∂ C = [ ∂ L ∂ i ∂ L ∂ j ∂ L ∂ k ∂ L ∂ l ] = [ w x y z ] A = \begin{bmatrix} a & b\\ c & d \end{bmatrix} \quad B = \begin{bmatrix} e & f \\ g & h \end{bmatrix} \quad C = \begin{bmatrix} i & j \\ k & l \end{bmatrix} \quad G = \frac{\partial L}{\partial C} = \begin{bmatrix} \frac{\partial L}{\partial i} & \frac{\partial L}{\partial j} \\ \frac{\partial L}{\partial k} & \frac{\partial L}{\partial l} \end{bmatrix} = \begin{bmatrix} w & x \\ y & z \end{bmatrix} A=[acbd]B=[egfh]C=[ikjl]G=CL=[iLkLjLlL]=[wyxz]

  5. 展开左边 A ⋅ B A \cdot B AB

C = [ i = a e + b g j = a f + b h k = c e + d g l = c f + d h ] C = \begin{bmatrix} i = ae + bg & j = af + bh\\ k = ce + dg & l = cf + dh \end{bmatrix} C=[i=ae+bgk=ce+dgj=af+bhl=cf+dh]

  1. L L L 对于每一个 A A A 的导数
    ▽ A i j = ∂ L ∂ A i j \triangledown A_{ij} = \frac{\partial L}{\partial A_{ij}} Aij=AijL

    ∂ L ∂ a = ∂ L ∂ i ∗ ∂ i ∂ a + ∂ L ∂ j ∗ ∂ j ∂ a ∂ L ∂ b = ∂ L ∂ i ∗ ∂ i ∂ b + ∂ L ∂ j ∗ ∂ j ∂ b ∂ L ∂ c = ∂ L ∂ k ∗ ∂ k ∂ c + ∂ L ∂ l ∗ ∂ l ∂ c ∂ L ∂ d = ∂ L ∂ k ∗ ∂ k ∂ d + ∂ L ∂ l ∗ ∂ l ∂ d \begin{aligned} \frac{\partial L}{\partial a} &= \frac{\partial L}{\partial i} * \frac{\partial i}{\partial a} + \frac{\partial L}{\partial j} * \frac{\partial j}{\partial a} \\ \frac{\partial L}{\partial b} &= \frac{\partial L}{\partial i} * \frac{\partial i}{\partial b} + \frac{\partial L}{\partial j} * \frac{\partial j}{\partial b} \\ \frac{\partial L}{\partial c} &= \frac{\partial L}{\partial k} * \frac{\partial k}{\partial c} + \frac{\partial L}{\partial l} * \frac{\partial l}{\partial c} \\ \frac{\partial L}{\partial d} &= \frac{\partial L}{\partial k} * \frac{\partial k}{\partial d} + \frac{\partial L}{\partial l} * \frac{\partial l}{\partial d} \end{aligned} aLbLcLdL=iLai+jLaj=iLbi+jLbj=kLck+lLcl=kLdk+lLdl

    ∂ L ∂ a = w e + x f ∂ L ∂ b = w g + x h ∂ L ∂ c = y e + z f ∂ L ∂ d = y g + z h \begin{aligned} \frac{\partial L}{\partial a} &= we + xf \\ \frac{\partial L}{\partial b} &= wg + xh \\ \frac{\partial L}{\partial c} &= ye + zf \\ \frac{\partial L}{\partial d} &= yg + zh \end{aligned} aLbLcLdL=we+xf=wg+xh=ye+zf=yg+zh

  2. 因此 A A A 的导数为

    ∂ L ∂ A = [ w e + x f w g + x h y e + z f y g + z h ] ∂ L ∂ A = [ w x y z ] [ e g f h ] \frac{\partial L}{\partial A} = \begin{bmatrix} we + xf & wg + xh\\ ye + zf & yg + zh \end{bmatrix} \quad \frac{\partial L}{\partial A} = \begin{bmatrix} w & x\\ y & z \end{bmatrix} \begin{bmatrix} e & g\\ f & h \end{bmatrix} AL=[we+xfye+zfwg+xhyg+zh]AL=[wyxz][efgh]

    ∂ L ∂ A = G ⋅ B T \frac{\partial L}{\partial A} = G \cdot B^T AL=GBT

  3. 同理 B B B 的导数为
    ∂ L ∂ e = w a + y c ∂ L ∂ f = x a + z c ∂ L ∂ g = w b + y d ∂ L ∂ h = x b + z d \begin{aligned} \frac{\partial L}{\partial e} &= wa + yc \\ \frac{\partial L}{\partial f} &= xa + zc \\ \frac{\partial L}{\partial g} &= wb + yd \\ \frac{\partial L}{\partial h} &= xb + zd \end{aligned} eLfLgLhL=wa+yc=xa+zc=wb+yd=xb+zd

    ∂ L ∂ B = [ w a + y c x a + z c w b + y d x b + z d ] ∂ L ∂ B = [ a c b d ] [ w x y z ] \frac{\partial L}{\partial B} = \begin{bmatrix} wa + yc & xa + zc\\ wb + yd & xb + zd \end{bmatrix} \quad \frac{\partial L}{\partial B} = \begin{bmatrix} a & c\\ b & d \end{bmatrix} \begin{bmatrix} w & x\\ y & z \end{bmatrix} BL=[wa+ycwb+ydxa+zcxb+zd]BL=[abcd][wyxz]

    ∂ L ∂ B = A T ⋅ G \frac{\partial L}{\partial B} = A^T \cdot G BL=ATG

2. 多元线性回归

之前我们是通过房价预测的案例来讲解线性回归的,在前面的分析中我们假设房价只与房屋面积一个自变量相关,因此可以将其视为一元线性回归模型处理,其对应的房价计算公式如下:

房价 = 房屋面积 * 系数 + 偏置

而实际情况下,房价应该与多个自变量相关,比如房屋的面积(m2)、距离地铁的远近(km)、装修的程度(0-1)等等,因此需要将其视为多元线性回归模型处理,其对应的房价计算公式如下:

房价 = 房屋面积 * 系数1 + 距离 * 系数2 + 装修 * 系数3 + 偏置

我们先初步写个 demo 确保各部分的维度是正确的,示例代码如下:

import numpy as np# 定义变量
# 面积,距离,装修
x = np.array([[80, 3, 1],[100, 5, 0.3],[130, 2, 0.1],[300, 20, 1],[60, 1, 0.8]
])# 定义输出
# 房价
y = np.array([50000,60000,50000,30000,70000,
]).reshape(-1, 1)k = np.random.randn(3).reshape(1, 3)
b = 0# x(5x3), k(1x3), b(1,)
predict = (x * k + b).sum(axis=1, keepdims=True)# predict(5x1) y(5x1)
loss = 0.5 * ((predict - y) ** 2).sum()
print(loss)

在上面的示例代码中,我们使用 numpy 来进行数值计算和对应的矩阵操作。首先定义了一个自变量 x(5,3),表示 5 个样本的特征值,每行包含房屋面积、距离和装修情况三个特征,并定义了因变量房价 y(5,1);接下来初始化权重参数 k(1,3) 和偏置 b(1,);然后利用矩阵运算的方式得出了每个样本的房价预测结果;最后根据预测结果和实际房价 y 之间的差异来计算 loss,使用的是均方差损失函数,最终得出的 loss 是一个标量,符合我们的预期。

我们确保了最终输出 loss 的 shape 是正确的,接下来我们来对其进行优化,包括对数据进行正则化、循环迭代更新参数以及验证

优化后的示例代码如下:

import numpy as np# 定义变量
# 面积,距离,装修
x = np.array([[80, 3, 1],[100, 5, 0.3],[130, 2, 0.1],[300, 20, 1],[60, 1, 0.8]
])# 定义输出
# 房价
y = np.array([50000,60000,50000,30000,70000,
]).reshape(-1, 1)k = np.random.randn(3).reshape(1, 3)
b = 0# 数据正则化
def normalize(x):x_std  = x.std(axis=0, keepdims=True)x_mean = x.mean(axis=0, keepdims=True)norm_x = (x - x_mean) / x_stdreturn norm_x, x_std, x_meannorm_x, x_std, x_mean = normalize(x)
norm_y, y_std, y_mean = normalize(y)
lr = 1e-2for iter in range(1000):# x(5x3), k(1x3), b(1,)predict = (norm_x * k + b).sum(axis=1, keepdims=True)# predict(5x1) y(5x1)loss = 0.5 * ((predict - norm_y) ** 2).sum()if iter % 100 == 0:print(f"Iter {iter}, Loss: {loss:.3f}")delta_k = ((predict - norm_y) * norm_x).sum(axis=0, keepdims=True)delta_b = (predict - norm_y).sum()# 参数更新k = k - lr * delta_kb = b - lr * delta_b# 验证
while True:text = input("请输入你想预测的房子的相关信息[面积,距离,装修]:")feature = list(map(float, text.strip().split(" ")))if len(feature) != 3:print(f"输入有误,请重新输入")continuefeature = np.array([feature])norm_feature = (feature - x_mean) / x_stdpredict = (norm_feature * k + b).sum()real_predict = predict * y_std + y_meanprint(real_predict)

上述示例代码是完整的多元线性回归的房价预测案例。通过给定的房屋特征(面积、距离、装修),使用梯度下降法不断调整权重系数和偏置,以最小化预测值与实际房价之间的损失。通过数据正则化和参数更新,预测新房屋的房价。值得一提的是,模型最终的效果似乎并没有我们想象的那么好😂,这并不重要,我们主要通过这个案例来学习多元线性回归模型。

运行效果如下:

在这里插入图片描述

图2-1 多元线性回归房价预测(单输出)

3. 多模型线性回归

在上面的分析中,我们考虑的是多变量单输出的情况,也就是最终我们只需要预测房价,如果是多变量多输出的情况又该如何考虑呢?🤔

假设目前我们根据面积、距离和装修程度三个特征不仅要预测房价还需要预测租金,也就是有两个输出,其实对应的就是两个模型,此时我们可以使用上节课学习到的矩阵知识来处理多个模型的问题。

具体的实现代码如下,与上述单输出的情况并没有太大的差异,只是需要从矩阵的角度来考虑这个问题

import numpy as np# 定义变量
# 面积,距离,装修
x = np.array([[80, 3, 1],[100, 5, 0.3],[130, 2, 0.1],[300, 20, 1],[60, 1, 0.8]
])# 定义输出
# 房价,租金
y = np.array([50000, 3000,50000, 6000,60000, 8000,30000, 9000,70000, 3000
]).reshape(-1, 2)# 权重系数和偏置
# 看作两个模型,每个模型包含3个权重和一个偏置
k = np.random.randn(3, 2)
b = np.zeros((1, 2))# 数据正则化
def normalize(x):x_std  = x.std(axis=0, keepdims=True)x_mean = x.mean(axis=0, keepdims=True)norm_x = (x - x_mean) / x_stdreturn norm_x, x_std, x_meannorm_x, x_std, x_mean = normalize(x)
norm_y, y_std, y_mean = normalize(y)
lr = 1e-3for iter in range(1000):# norm_x(5x3), k(3x2), b(1x2)# predict(5x2)predict = norm_x @ k + b# predict(5x2) y(5x2)loss = 0.5 * ((predict - norm_y) ** 2).sum()if iter % 100 == 0:print(f"Iter {iter}, Loss: {loss:.3f}")# predict(5x2), norm_y(5x2)# C = AB# dA = G @ B.T# dB = A.T @ GG = predict - norm_ydelta_k = norm_x.T @ Gdelta_b = G.sum(axis=0, keepdims=True)# 参数更新k = k - lr * delta_kb = b - lr * delta_b# 验证
while True:text = input("请输入你想预测的房子的相关信息[面积,距离,装修]:")feature = list(map(float, text.strip().split(" ")))if len(feature) != 3:print(f"输入有误,请重新输入")continuefeature = np.array([feature])norm_feature = (feature - x_mean) / x_std# 1x2predict = norm_feature @ k + breal_predict = predict * y_std + y_meanprice1, price2 = real_predict[0]print(f"对于房价的预测为:{price1:.3f},对于租金预测为:{price2:.3f}")

上述示例代码是一个多输出多元线性回归的示例,旨在通过给定的房屋特征(面积、距离、装修)同时预测房价和租金。这种多输出的线性回归模型适用于需要同时预测多个相关输出的场景。

运行效果如下:

在这里插入图片描述

图3-1 多元线性回归房价预测(多输出)

我们再来回顾下其中使用到的矩阵求导的相关知识

对于 A ⋅ B = C A \cdot B = C AB=C 定义 L L L 是关于 C C C 的损失函数

G = ∂ L ∂ C G = \dfrac{\partial L}{\partial C} G=CL,则有
∂ L ∂ A = G ⋅ B T ∂ L ∂ B = A T ⋅ G \dfrac{\partial L}{\partial A}=G\cdot B^T \ \ \ \ \dfrac{\partial L}{\partial B}=A^T \cdot G AL=GBT    BL=ATG
回到代码中,我们需要求 ∂ L ∂ k \dfrac{\partial L}{\partial k} kLdelta_k,其中 G = ∂ L ∂ C = p r e d i c t − n o r m _ y G = \dfrac{\partial L}{\partial C} = predict-norm\_y G=CL=predictnorm_y C = A ⋅ B = n o r m _ x ⋅ k C = A \cdot B = norm\_x \cdot k C=AB=norm_xk,因此最终的 ∂ L ∂ k = n o r m _ x . T ⋅ G \dfrac{\partial L}{\partial k} = norm\_x.T \cdot G kL=norm_x.TG

我们也可以从 shape 维度来验证下,norm_x(5x3) G(5x2) => norm_x.T(3x5) => delta_k(3x2)

4. 多分类逻辑回归

之前我们是通过苹果分类案例来讲解逻辑回归问题的,可以将其视为一个二分类问题,为此我们引入了 sigmoid 函数并定义了逻辑回归模型的损失函数即二元交叉熵损失函数。

这节课我们来学习多分类逻辑回归,对 mnist 手写识别数据集中的 10 个类别进行分类。

在正式开始之前,我们需要考虑如下问题:

  1. 由于是 10 个数字的分类任务,所以可以将其视为 10 个逻辑回归模型

  2. 关于正则化的考虑

  • 我们是否应该参考线性回归的正则化,对每个 pixel 都统计其 mean 和 std 呢?🤔
  • 如果每个数据都是独立分布,则需要独立统计 mean 和 std,而对于 2d 图像,每个 pixel 并不是独立分布的,它们是存在空间相关性的,因此需要统一考虑 mean 和 std。对于训练集,需要统计 mean 和 std,对于测试集,使用训练集统计的 mean 和 std 即可
  1. 我们之前使用 sigmoid 处理二分类问题,即 sigmoid(predict)->logits(0-1),在这里是多分类问题,我们考虑使用 softmax(predict)->logits(0-1)

我们先把前面写过的 mnist 的 dataset 和 dataloader 的示例代码 copy 过来,如下所示:

import numpy as np
import matplotlib.pyplot as pltclass MNISTDataset:def __init__(self, images_path, labels_path):self.images_path = images_pathself.labels_path = labels_pathself.images, self.labels = self.load_mnist_data()def __len__(self):return len(self.images)def __getitem__(self, index):image, label = self.images[index], self.labels[index]return image, labeldef load_mnist_data(self):# load labelswith open(self.labels_path, "rb") as f:magic_number, num_of_items = np.frombuffer(f.read(8), dtype=">i", count=2, offset=0)labels = np.frombuffer(f.read(), dtype=np.uint8, count=-1, offset=0)# load imageswith open(self.images_path, "rb") as f:magic_number, num_of_images, rows, cols = np.frombuffer(f.read(16), dtype=">i", count=4, offset=0)pixels = np.frombuffer(f.read(), dtype=np.uint8, count=-1, offset=0)images_matrix = pixels.reshape(num_of_images, rows, cols)return images_matrix, labelsclass MNISTDataLoader:def __init__(self, dataset, batch_size, shuffle=True):self.dataset = datasetself.batch_size = batch_sizeself.shuffle = shuffledef __iter__(self):self.indexs = np.arange(len(self.dataset))if self.shuffle:np.random.shuffle(self.indexs)self.cursor = 0return selfdef __next__(self):begin = self.cursorend   = self.cursor + self.batch_sizeif end > len(self.dataset):raise StopIteration()self.cursor = endbatched_data = []for index in self.indexs[begin:end]:item = self.dataset[index]batched_data.append(item)return batched_datadef show_mnist_image(images, labels, n=1):for i in range(n):image = images[i]label = labels[i]plt.title(f"label is {label}")plt.imshow(image)plt.show()# example usage
dataset = MNISTDataset("mnist/t10k-images-idx3-ubyte", "mnist/t10k-labels-idx1-ubyte")
loader  = MNISTDataLoader(dataset, batch_size=32, shuffle=True)
for i, batched_data in enumerate(loader):if i == 0:images, labels = list(zip(*batched_data))print(len(batched_data))show_mnist_image(images, labels)        

首先是正则化的考虑,计算 mean 和 std

class MNISTDataset:def __init__(self, images_path, labels_path, train, mean=None, std=None):self.images_path = images_pathself.labels_path = labels_pathself.images, self.labels = self.load_mnist_data()# flatten Nx28x28 -> Nx784self.images =  self.images.reshape(len(self.images), -1)if train:self.images, self.mean, self.std = self.normalize(self.images)else:self.images, self.mean, self.std = self.normalize(self.images, mean, std)@staticmethoddef normalize(x, mean=None, std=None):if mean is None:mean = x.mean()if std is None:std = x.std()x = (x - mean) / stdreturn x, mean, std...

然后是考虑 softmax 来处理多分类问题,其计算公式如下:
σ ( z ) j = e z j ∑ k = 1 K e z k f o r j = 1 , . . . , K . \sigma(\mathbf{z})_{j}={\frac{e^{z_{j}}}{\sum_{k=1}^{K}e^{z_{k}}}}\quad{\mathrm{for}}\ \ j=1,...,K. σ(z)j=k=1Kezkezjfor  j=1,...,K.
假设当前输入 x = [x0, x1, x2],经过 softmax 后的输出是怎样的呢?

我们定义 esum = exp(x0) + exp(x1) + exp(x2)

故最终的输出为 output = [exp(x0)/esum,exp(x1)/esum,exp(x2)/esum]

我们还需要考虑一个溢出问题, 假设当前的输入 x = [100, 500, 1000],exp(1000)会导致溢出,而我们常见的解决手段是将输入中的每个值减去其中的最大值即 x-x.max()

对应的 softmax crossentropy loss 的计算如下:
l o s s = − [ y ∗ ln ⁡ ( p ) ] loss = -[y * \ln(p)] loss=[yln(p)]
关于 loss 的导数推导具体可参考 一文详解Softmax函数

还有一点值得注意的是对于 softmax 而言,labels 需要转换为 onehot 编码的格式

通过上述分析,循环更新迭代的示例代码如下所示:

# 定义k和b
num_classes = 10
k = np.random.randn(784, num_classes)
b = np.zeros((1, num_classes))def softmax(x, dim):x = np.exp(x - x.max())return x / x.sum(axis=dim, keepdims=True)def crossentropy_loss(logits, onehot_labels):batch = logits.shape[0]return -(onehot_labels * np.log(logits)).sum() / batchlr = 1e-2
niter = 0for epoch in range(10):for images, labels in train_loader:niter += 1# 32x784 @ 784x10predict = images @ k + b# predict -> logitslogits = softmax(predict, dim=1)# softmax crossentropy loss# loss = -(y * ln(p))batch = logits.shape[0]onehot_labels = np.zeros_like(logits)# labels(32,) -> onehot(32,10)onehot_labels[np.arange(batch), labels] = 1loss = crossentropy_loss(logits, onehot_labels)if niter % 100 == 0:print(f"Epoch: {epoch}, Iter: {niter}, Loss: {loss:.3f}")G = (logits - onehot_labels) / batch# C = AB# dA = G @ B.T# dB = A.T @ Gdelta_k = images.T @ Gdelta_b = G.sum(axis=0, keepdims=True)k = k - lr * delta_kb = b - lr * delta_b# evaluateall_predict = []for images, labels in test_loader:predict = images @ k + blogits = softmax(predict, dim=1)predict_labels = logits.argmax(axis=1)all_predict.extend(predict_labels == labels)accuracy = np.sum(all_predict) / len(all_predict) * 100print(f"Epoch: {epoch}, Evaluate Test Set, Accuracy: {accuracy:.3f} %")

上述示例代码展示了 MNIST 数据集上使用逻辑回归进行多分类的示例。它使用 softmax 函数将模型输出转换为概率分布,并使用交叉熵损失函数衡量模型输出与真实标签之间的差异。通过梯度下降法更新权重和偏置,以最小化损失函数。我们还使用测试集计算模型的准确率,并通过迭代训练和评估,可以逐步优化模型,并得到在 MNIST 数据集上分类准确的结果。

运行效果如下:

在这里插入图片描述

图4-1 逻辑回归多分类

为了更好的查看结果,我们可以对其进行可视化操作,具体实现代码如下:

for images, labels in test_loader:predict = images @ k + blogits = softmax(predict, dim=1)predict_labels = logits.argmax(axis=1)pixels = (images * train_dataset.std + train_dataset.mean).astype(np.uint8).reshape(-1, 28, 28)for image, predict, gt in zip(pixels, predict_labels, labels):plt.imshow(image)plt.title(f"Predict: {predict}, GT: {gt}")plt.show()

需要注意的是,在可视化时需要将对应图片的像素值通过 mean 和 std 恢复到原始范围,并将其 reshape 为 28x28 的大小,方便可视化。

模型预测的部分结果如下所示:

在这里插入图片描述

图4-2 预测结果1

在这里插入图片描述

图4-3 预测结果2

可以看到模型预测的部分结果还是有误的,这也很正常,毕竟分类识别准确率在 86.6% 左右。

完整的示例代码如下所示:

import numpy as np
import matplotlib.pyplot as pltclass MNISTDataset:def __init__(self, images_path, labels_path, train, mean=None, std=None):self.images_path = images_pathself.labels_path = labels_pathself.images, self.labels = self.load_mnist_data()# flatten Nx28x28 -> Nx784self.images =  self.images.reshape(len(self.images), -1)if train:self.images, self.mean, self.std = self.normalize(self.images)else:self.images, self.mean, self.std = self.normalize(self.images, mean, std)@staticmethoddef normalize(x, mean=None, std=None):if mean is None:mean = x.mean()if std is None:std = x.std()x = (x - mean) / stdreturn x, mean, stddef __len__(self):return len(self.images)def __getitem__(self, index):image, label = self.images[index], self.labels[index]return image, labeldef load_mnist_data(self):# load labelswith open(self.labels_path, "rb") as f:magic_number, num_of_items = np.frombuffer(f.read(8), dtype=">i", count=2, offset=0)labels = np.frombuffer(f.read(), dtype=np.uint8, count=-1, offset=0)# load imageswith open(self.images_path, "rb") as f:magic_number, num_of_images, rows, cols = np.frombuffer(f.read(16), dtype=">i", count=4, offset=0)pixels = np.frombuffer(f.read(), dtype=np.uint8, count=-1, offset=0)images_matrix = pixels.reshape(num_of_images, rows, cols)return images_matrix, labelsclass MNISTDataLoader:def __init__(self, dataset, batch_size, shuffle=True):self.dataset = datasetself.batch_size = batch_sizeself.shuffle = shuffledef __iter__(self):self.indexs = np.arange(len(self.dataset))if self.shuffle:np.random.shuffle(self.indexs)self.cursor = 0return selfdef __next__(self):begin = self.cursorend   = self.cursor + self.batch_sizeif end > len(self.dataset):raise StopIteration()self.cursor = endbatched_data = []for index in self.indexs[begin:end]:item = self.dataset[index]batched_data.append(item)# return batched_datareturn [np.stack(item, axis=0) for item in list(zip(*batched_data))]      # 训练集
train_dataset = MNISTDataset("mnist/train-images-idx3-ubyte", "mnist/train-labels-idx1-ubyte", train=True)
train_loader  = MNISTDataLoader(train_dataset, batch_size=32, shuffle=True)# 测试集
test_dataset = MNISTDataset("mnist/t10k-images-idx3-ubyte", "mnist/t10k-labels-idx1-ubyte",train=False,mean=train_dataset.mean,std=train_dataset.std
)
test_loader = MNISTDataLoader(test_dataset, 10)# 定义k和b
num_classes = 10
k = np.random.randn(784, num_classes)
b = np.zeros((1, num_classes))def softmax(x, dim):x = np.exp(x - x.max())return x / x.sum(axis=dim, keepdims=True)def crossentropy_loss(logits, onehot_labels):batch = logits.shape[0]return -(onehot_labels * np.log(logits)).sum() / batchlr = 1e-2
niter = 0for epoch in range(10):for images, labels in train_loader:niter += 1# 32x784 @ 784x10predict = images @ k + b# predict -> logitslogits = softmax(predict, dim=1)# softmax crossentropy loss# loss = -(y * ln(p))batch = logits.shape[0]onehot_labels = np.zeros_like(logits)# labels(32,) -> onehot(32,10)onehot_labels[np.arange(batch), labels] = 1loss = crossentropy_loss(logits, onehot_labels)if niter % 100 == 0:print(f"Epoch: {epoch}, Iter: {niter}, Loss: {loss:.3f}")G = (logits - onehot_labels) / batch# C = AB# dA = G @ B.T# dB = A.T @ Gdelta_k = images.T @ Gdelta_b = G.sum(axis=0, keepdims=True)k = k - lr * delta_kb = b - lr * delta_b# evaluateall_predict = []for images, labels in test_loader:predict = images @ k + blogits = softmax(predict, dim=1)predict_labels = logits.argmax(axis=1)all_predict.extend(predict_labels == labels)accuracy = np.sum(all_predict) / len(all_predict) * 100print(f"Epoch: {epoch}, Evaluate Test Set, Accuracy: {accuracy:.3f} %")for images, labels in test_loader:predict = images @ k + blogits = softmax(predict, dim=1)predict_labels = logits.argmax(axis=1)pixels = (images * train_dataset.std + train_dataset.mean).astype(np.uint8).reshape(-1, 28, 28)for image, predict, gt in zip(pixels, predict_labels, labels):plt.imshow(image)plt.title(f"Predict: {predict}, GT: {gt}")plt.show()

总结

本次课程学习了矩阵求导相关知识,后续实现多个线性回归或者逻辑逻辑回归模型可以考虑使用矩阵方式来表达

本次课程主要学习了多元和多模型线性回归,还是从房价预测的案例出发,考虑多个变量影响房价,从而引出多元线性回归模型;考虑多个输出即多个模型,不仅预测房价还预测租金,引出多模型线性回归,从矩阵角度对其进行分析,重新回顾并学习了矩阵求导的相关知识。

本次课程主要学习了多分类逻辑回归模型,对手写识别数据集进行分类。我们对于正则化的考虑是对训练集只使用一个 mean 和 std,因为我们认为每个 pixel 之间并不是相互独立的,而需要将其视为一个整体,而对于测试集的 mean 和 std 我们直接使用训练集的即可。此外,与二分类不同的是我们使用 softmax 函数将模型的输出转换为概率分布,并使用 softmax crossentropy loss 作为该多分类逻辑回归模型的损失函数。

这篇关于AutoCV第九课:ML基础的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/187412

相关文章

从基础到进阶详解Pandas时间数据处理指南

《从基础到进阶详解Pandas时间数据处理指南》Pandas构建了完整的时间数据处理生态,核心由四个基础类构成,Timestamp,DatetimeIndex,Period和Timedelta,下面我... 目录1. 时间数据类型与基础操作1.1 核心时间对象体系1.2 时间数据生成技巧2. 时间索引与数据

安装centos8设置基础软件仓库时出错的解决方案

《安装centos8设置基础软件仓库时出错的解决方案》:本文主要介绍安装centos8设置基础软件仓库时出错的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录安装Centos8设置基础软件仓库时出错版本 8版本 8.2.200android4版本 javas

Linux基础命令@grep、wc、管道符的使用详解

《Linux基础命令@grep、wc、管道符的使用详解》:本文主要介绍Linux基础命令@grep、wc、管道符的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录grep概念语法作用演示一演示二演示三,带选项 -nwc概念语法作用wc,不带选项-c,统计字节数-

python操作redis基础

《python操作redis基础》Redis(RemoteDictionaryServer)是一个开源的、基于内存的键值对(Key-Value)存储系统,它通常用作数据库、缓存和消息代理,这篇文章... 目录1. Redis 简介2. 前提条件3. 安装 python Redis 客户端库4. 连接到 Re

SpringBoot基础框架详解

《SpringBoot基础框架详解》SpringBoot开发目的是为了简化Spring应用的创建、运行、调试和部署等,使用SpringBoot可以不用或者只需要很少的Spring配置就可以让企业项目快... 目录SpringBoot基础 – 框架介绍1.SpringBoot介绍1.1 概述1.2 核心功能2

Spring Boot集成SLF4j从基础到高级实践(最新推荐)

《SpringBoot集成SLF4j从基础到高级实践(最新推荐)》SLF4j(SimpleLoggingFacadeforJava)是一个日志门面(Facade),不是具体的日志实现,这篇文章主要介... 目录一、日志框架概述与SLF4j简介1.1 为什么需要日志框架1.2 主流日志框架对比1.3 SLF4

Spring Boot集成Logback终极指南之从基础到高级配置实战指南

《SpringBoot集成Logback终极指南之从基础到高级配置实战指南》Logback是一个可靠、通用且快速的Java日志框架,作为Log4j的继承者,由Log4j创始人设计,:本文主要介绍... 目录一、Logback简介与Spring Boot集成基础1.1 Logback是什么?1.2 Sprin

MySQL复合查询从基础到多表关联与高级技巧全解析

《MySQL复合查询从基础到多表关联与高级技巧全解析》本文主要讲解了在MySQL中的复合查询,下面是关于本文章所需要数据的建表语句,感兴趣的朋友跟随小编一起看看吧... 目录前言:1.基本查询回顾:1.1.查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J1.2.按照部门

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

mysql的基础语句和外键查询及其语句详解(推荐)

《mysql的基础语句和外键查询及其语句详解(推荐)》:本文主要介绍mysql的基础语句和外键查询及其语句详解(推荐),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录一、mysql 基础语句1. 数据库操作 创建数据库2. 表操作 创建表3. CRUD 操作二、外键