深度学习算法原理实现——线性分类器

 

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt


def model(inputs):
    return tf.matmul(inputs, W) + b

def square_loss(targets, predictions):
    per_sample_losses = tf.square(targets - predictions)
    return tf.reduce_mean(per_sample_losses)

learning_rate = 0.1

def training_step(inputs, targets):
    with tf.GradientTape() as tape:
        predictions = model(inputs)
        loss = square_loss(targets, predictions)
    grad_loss_wrt_W, grad_loss_wrt_b = tape.gradient(loss, [W, b])
    W.assign_sub(grad_loss_wrt_W * learning_rate)
    b.assign_sub(grad_loss_wrt_b * learning_rate)
    return loss

num_samples_per_class = 1000
negative_samples = np.random.multivariate_normal(
    mean=[0, 3],
    cov=[[1, 0.5],[0.5, 1]],
    size=num_samples_per_class)
positive_samples = np.random.multivariate_normal(
    mean=[3, 0],
    cov=[[1, 0.5],[0.5, 1]],
    size=num_samples_per_class)
inputs = np.vstack((negative_samples, positive_samples)).astype(np.float32)
targets = np.vstack((np.zeros((num_samples_per_class, 1), dtype="float32"),
                     np.ones((num_samples_per_class, 1), dtype="float32")))
plt.scatter(inputs[:, 0], inputs[:, 1], c=targets[:, 0])
plt.show()

input_dim = 2
output_dim = 1
W = tf.Variable(initial_value=tf.random.uniform(shape=(input_dim, output_dim)))
b = tf.Variable(initial_value=tf.zeros(shape=(output_dim,)))
for step in range(40):
    loss = training_step(inputs, targets)
    print(f"Loss at step {step}: {loss:.4f}")

predictions = model(inputs)
plt.scatter(inputs[:, 0], inputs[:, 1], c=predictions[:, 0] > 0.5)
plt.show()

x = np.linspace(-1, 4, 100)
y = - W[0] /  W[1] * x + (0.5 - b) / W[1]
plt.plot(x, y, "-r")
plt.scatter(inputs[:, 0], inputs[:, 1], c=predictions[:, 0] > 0.5)
plt.show()

  

效果图:

【代码分析】

这段代码实现了一个简单的线性分类器,用于二分类问题。主要使用了 TensorFlow 库进行实现。

以下是代码的主要部分和它们的功能:

1. 定义模型:model 函数定义了一个线性模型,输入乘以权重 W 加上偏置 b。

2. 定义损失函数:square_loss 函数定义了平方损失函数,计算目标值和预测值之间的差的平方的均值。

3. 定义训练步骤:training_step 函数定义了一步训练过程,包括前向传播、计算损失、反向传播和更新权重。

4. 生成训练数据:生成了两类样本,每类样本1000个,分别从两个二维正态分布中采样。

5. 初始化模型参数:初始化了权重 W 和偏置 b。

6. 训练模型:进行了40轮训练,每轮训练都会计算损失并更新权重。

7. 测试模型:使用训练数据对模型进行了测试,并绘制了分类结果。

8. 绘制决策边界:计算了决策边界的方程,并在图中绘制了决策边界。

这段代码的主要目的是展示如何使用 TensorFlow 实现一个简单的线性分类器,并用它进行二分类问题的训练和测试。

 

【该分类器和svm的区别】

 这个线性分类器和支持向量机(SVM)都是用于进行二分类任务的模型,但它们在模型构造和优化目标上有一些不同:

1. 模型构造:这个线性分类器是一个简单的线性模型,它直接将输入特征与权重相乘,然后加上偏置,得到预测结果。而 SVM 不仅包括线性 SVM,还有非线性 SVM,非线性 SVM 通过使用核函数将输入特征映射到高维空间,然后在高维空间中找到最优的分割超平面。

2. 优化目标:这个线性分类器使用的是平方损失函数,优化目标是最小化预测值与真实值之间的平方差。而 SVM 使用的是合页损失函数,优化目标是最大化分类间隔,即找到一个超平面使得距离超平面最近的样本点(支持向量)到超平面的距离最大。

3. 决策边界:这个线性分类器的决策边界是由所有样本共同决定的,每个样本的改变都可能影响决策边界。而 SVM 的决策边界只由支持向量决定,非支持向量的样本即使有所改变,也不会影响决策边界。

4. 鲁棒性:由于 SVM 只关注支持向量,因此对于噪声和异常值具有较好的鲁棒性。而这个线性分类器由于使用的是平方损失函数,对于远离决策边界的样本(可能是噪声或异常值)会给予较大的权重,因此鲁棒性较差。

总的来说,这个线性分类器和 SVM 在一些基本概念上是相似的,但在具体的实现和优化目标上有所不同。

 

我们看下预测predictions的值:

tf.Tensor(
[[-0.09647062]
 [ 0.16270795]
 [ 0.2278583 ]
 ...
 [ 1.1228456 ]
 [ 1.0326965 ]
 [ 0.79803073]], shape=(2000, 1), dtype=float32)

  

可以看到,类似一个回归,而不是严格的分类(结果为0和1)!

还记得svm的预测代码吗?

    def predict(self, X):
        return self.h(X, self.w, self.b)
     
    def h(self, X, w, b):
        return np.sign(np.dot(w.T, X.T) + b).astype(int)

  

详见:机器学习算法原理实现——跟着gpt学习svm求解的SMO算法

 

为了对比和使用原生keras的效果,如下:

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt


def model(inputs):
    return tf.matmul(inputs, W) + b

def square_loss(targets, predictions):
    per_sample_losses = tf.square(targets - predictions)
    return tf.reduce_mean(per_sample_losses)

learning_rate = 0.1

def training_step(inputs, targets):
    with tf.GradientTape() as tape:
        predictions = model(inputs)
        loss = square_loss(targets, predictions)
    grad_loss_wrt_W, grad_loss_wrt_b = tape.gradient(loss, [W, b])
    W.assign_sub(grad_loss_wrt_W * learning_rate)
    b.assign_sub(grad_loss_wrt_b * learning_rate)
    return loss

num_samples_per_class = 1000
negative_samples = np.random.multivariate_normal(
    mean=[0, 3],
    cov=[[1, 0.5],[0.5, 1]],
    size=num_samples_per_class)
positive_samples = np.random.multivariate_normal(
    mean=[3, 0],
    cov=[[1, 0.5],[0.5, 1]],
    size=num_samples_per_class)
inputs = np.vstack((negative_samples, positive_samples)).astype(np.float32)
targets = np.vstack((np.zeros((num_samples_per_class, 1), dtype="float32"),
                     np.ones((num_samples_per_class, 1), dtype="float32")))
plt.scatter(inputs[:, 0], inputs[:, 1], c=targets[:, 0])
plt.show()

input_dim = 2
output_dim = 1
W = tf.Variable(initial_value=tf.random.uniform(shape=(input_dim, output_dim)))
b = tf.Variable(initial_value=tf.zeros(shape=(output_dim,)))
for step in range(40):
    loss = training_step(inputs, targets)
    print(f"Loss at step {step}: {loss:.4f}")

predictions = model(inputs)
plt.scatter(inputs[:, 0], inputs[:, 1], c=predictions[:, 0] > 0.5)
print(predictions)
plt.show()

x = np.linspace(-1, 4, 100)
y = - W[0] /  W[1] * x + (0.5 - b) / W[1]
plt.plot(x, y, "-r")
plt.scatter(inputs[:, 0], inputs[:, 1], c=predictions[:, 0] > 0.5)
plt.show()


import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import SGD
import matplotlib.pyplot as plt

def square_loss(y_true, y_pred):
    return tf.reduce_mean(tf.square(y_true - y_pred))

# 定义模型
model = Sequential([
    Dense(1, input_dim=2, activation=None)
])
model.compile(optimizer=SGD(learning_rate=0.1), loss=square_loss)
# 编译模型
# model.compile(optimizer=SGD(learning_rate=0.1), loss='mse')
# 训练模型
model.fit(inputs, targets, epochs=40, batch_size=len(inputs))
# 测试模型
predictions = model.predict(inputs)
# 绘制结果
plt.scatter(inputs[:, 0], inputs[:, 1], c=predictions[:, 0] > 0.5)
plt.show()

# 获取权重和偏置
weights, biases = model.layers[0].get_weights()
# 打印权重和偏置
print("Weights:\n", weights)
print("Biases:\n", biases)
print("Customized model weight:", W, "b:", b)

  

输出:

Weights:
 [[ 0.18331909]
 [-0.11702461]]
Biases:
 [0.36755753]
Customized model weight: <tf.Variable 'Variable:0' shape=(2, 1) dtype=float32, numpy=
array([[ 0.18086615],
       [-0.11948624]], dtype=float32)> b: <tf.Variable 'Variable:0' shape=(1,) dtype=float32, numpy=array([0.37705055], dtype=float32)>
二者的w和b基本上没有差别!
 
posted @ 2023-10-08 15:32  bonelee  阅读(77)  评论(0编辑  收藏  举报