深度学习笔记

1. 深度学习简介

一个学校有 nn 门考试,每一门考试所占权重为 WnW_n, 对于一个学生,他的每一门考试的分数为 xnx_n, 再加上一个固定的常量 bb, 学校用于判断是否录取这个学生的逻辑为:

y^=W1x1+W2x2++Wnxn+b\hat{y} = W_1 x_1 + W_2 x_2 + \dots + W_n x_n + b

缩写为:

y^=Wx+b\hat{y} = Wx + b

由于最终所期望的计算结果 y^\hat{y} 为一个数值,所以 WW 是一个 1×n1 \times n 的矩阵:

W=[W1,W2,,Wn]\mathbf{W} = [W_1, W_2, \dots, W_n]

xx 则是一个 n×1n \times 1 的矩阵(如下所示),这样进行矩阵乘法 W×xW \times x 才会得到一个 1×11 \times 1 的矩阵,即 y^\hat{y}

x=[x1x2xn]\mathbf{x} = \begin{bmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{bmatrix}

y^\hat{y} 的值则被称之为预测值,而 yy 则被称之为真实值,学校会根据这个值来判断是否录取这个学生。深度学习的目的就是找到一组 WWbb 的值,使得预测值 y^\hat{y} 与真实值 yy 之间的差距最小。

2. 感知器 (Perceptron)

在一个坐标系中有两类点,一类是红色,一类是蓝色,每个点都有两个坐标 (x1,x2)(x_1, x_2),现在需要找到一条直线,将这两类点分开。

首先,我们假设这条直线的方程为:

y=W1x1+W2x2+b=0y = W_1 x_1 + W_2 x_2 + b = 0

其中 W1W_1W2W_2 是直线的斜率,bb 是直线的截距。

最开始,我们先随机初始化 W1W_1, W2W_2bb 的值,然后根据这个方程计算出每个点的 yy 值,如果 yy 值大于 0,则将这个点归为红色,如果 yy 值小于 0,则将这个点归为蓝色。

然后我们对每个点进行分类,如果分类错误,则调整 W1W_1, W2W_2bb 的值,直到所有点都被正确分类。调整的逻辑为:

W1=W1+α×(yy^)×x1W_1 = W_1 + \alpha \times (y - \hat{y}) \times x_1 W2=W2+α×(yy^)×x2W_2 = W_2 + \alpha \times (y - \hat{y}) \times x_2 b=b+α×(yy^)b = b + \alpha \times (y - \hat{y})

其中 α\alpha 是一个学习率,用来控制每次调整的幅度。在课程种提供了代码,可以参考代码来实现感知器。

首先我们有一个 csv 数据集,数据集的格式为:

0.78051,-0.063669,1
0.28774,0.29139,1
0.40714,0.17878,0
0.2923,0.4217,0
...

其中第一列和第二列是特征 x1x_1x2x_2,第三列是标签 yys,标签为 1 表示红色,标签为 0 表示蓝色。

然后我们读取数据集,并进行处理,代码如下:

import matplotlib.pyplot as plt
import numpy as np
import pandas


def stepFunction(t):
    return 1 if t >= 0 else 0


def prediction(X: np.ndarray, W: np.ndarray, b: float) -> int:
    return stepFunction((np.matmul(X, W) + b)[0])


def perceptronStep(
    X: np.ndarray,
    y: np.ndarray,
    W: np.ndarray,
    b: float,
    learn_rate: float = 0.01,
) -> tuple[np.ndarray, float]:
    for i, x_n in enumerate(X):
        y_hat = prediction(x_n, W, b)
        match y[i] - y_hat:
            case 1:
                W[0] += x_n[0] * learn_rate
                W[1] += x_n[1] * learn_rate
                b += learn_rate
            case -1:
                W[0] -= x_n[0] * learn_rate
                W[1] -= x_n[1] * learn_rate
                b -= learn_rate
    return W, b


def trainPerceptronAlgorithm(
    X: np.ndarray,
    y: np.ndarray,
    learn_rate: float = 0.01,
    num_epochs: int = 25,
) -> list[tuple[float, float]]:
    x_min, x_max = min(X.T[0]), max(X.T[0])
    y_min, y_max = min(X.T[1]), max(X.T[1])
    weights = np.array(np.random.rand(2, 1))
    bias = np.random.rand(1)[0] + x_max

    boundary_lines = []
    for i in range(num_epochs):
        weights, b = perceptronStep(X, y, weights, bias, learn_rate)
        boundary_lines.append((-weights[0] / weights[1], -b / weights[1]))

    # draw all the boundary lines
    x_vals = np.linspace(x_min, x_max, 100)
    line_n = 10
    for i, (m, b) in enumerate(boundary_lines[-line_n:]):
        y_vals = m * x_vals + b
        color = "b" if i < line_n - 1 else "r"
        line_style = "--" if i < line_n - 1 else "-"
        plt.plot(x_vals, y_vals, color=color, linestyle=line_style)

    # plot the data points
    plt.scatter(X[:, 0], X[:, 1], c=["b" if y_n else "r" for y_n in y])
    plt.xlim(x_min, x_max)
    plt.ylim(y_min, y_max)
    plt.show()

    return boundary_lines
def load_data() -> tuple[np.ndarray, np.ndarray]:
    df = pandas.read_csv("data.csv")
    X = df[["x", "y"]].to_numpy()
    y = df["label"].to_numpy()
    return X, y


def main():
    X, y = load_data()
    trainPerceptronAlgorithm(X, y, num_epochs=25)

其中,最核心的函数是 perceptronStep,这个函数会调用 prediction 函数来计算每个点的预测值,然后根据预测值和真实值的差距来调整 W1W_1, W2W_2bb 的值。而 trainPerceptronAlgorithm 函数则连续调用 perceptronStep 函数,num_epochs 参数用来控制迭代次数,learn_rate 参数用来控制每次调整的幅度,最终会绘制出每次迭代(训练)后的直线,并展示出来。此时我们得到的 W1W_1, W2W_2bb 的值就是最终的模型参数。