作业实战:实现一个单神经元网络

1. 单神经元网络的基本概念

1.1 什么是单神经元网络

单神经元网络(Single Neuron Network)是最简单的神经网络模型,它由一个输入层和一个输出层组成,其中输出层只有一个神经元。

1.2 单神经元网络的结构

单神经元网络的结构包括:

  • 输入层:接收输入特征
  • 权重:连接输入和神经元的参数
  • 偏置:神经元的偏置参数
  • 激活函数:对线性组合结果进行非线性变换
  • 输出层:产生最终输出

1.3 单神经元网络的数学表达式

单神经元网络的数学表达式为:

z = w1*x1 + w2*x2 + ... + wn*xn + b
y = f(z)

其中,w1, w2, ..., wn是权重,b是偏置,f是激活函数。

2. 单神经元网络的实现步骤

2.1 数据准备

首先,我们需要准备用于训练和测试的数据。

2.2 初始化参数

初始化网络的权重和偏置。

2.3 前向传播

计算网络的前向传播过程,得到预测输出。

2.4 计算损失

计算预测输出与真实标签之间的损失。

2.5 反向传播

计算损失函数对权重和偏置的梯度。

2.6 参数更新

使用梯度下降法更新权重和偏置。

2.7 模型评估

在测试集上评估模型的性能。

3. 实战案例:实现单神经元网络解决二分类问题

3.1 问题描述

我们将实现一个单神经元网络,用于解决二分类问题。具体来说,我们将创建一个线性可分的数据集,然后训练单神经元网络对数据进行分类。

3.2 数据准备

首先,我们创建一个线性可分的二分类数据集:

import numpy as np
import matplotlib.pyplot as plt

# 设置随机种子,确保结果可复现
np.random.seed(42)

# 创建数据集
def create_dataset(n_samples=100):
    # 创建两类数据
    class0 = np.random.normal( loc=(2, 2), scale=1, size=(n_samples//2, 2) )
    class1 = np.random.normal( loc=(5, 5), scale=1, size=(n_samples//2, 2) )
    
    # 创建标签
    labels0 = np.zeros( (n_samples//2, 1) )
    labels1 = np.ones( (n_samples//2, 1) )
    
    # 合并数据和标签
    X = np.vstack( (class0, class1) )
    y = np.vstack( (labels0, labels1) )
    
    # 打乱数据
    indices = np.random.permutation(n_samples)
    X = X[indices]
    y = y[indices]
    
    return X, y

# 创建数据集
X, y = create_dataset()

# 可视化数据集
plt.scatter(X[y.flatten() == 0][:, 0], X[y.flatten() == 0][:, 1], color='blue', label='Class 0')
plt.scatter(X[y.flatten() == 1][:, 0], X[y.flatten() == 1][:, 1], color='red', label='Class 1')
plt.title('Binary Classification Dataset')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()

3.3 单神经元网络的实现

现在,我们实现一个单神经元网络:

class SingleNeuron:
    def __init__(self, input_size):
        """
        初始化单神经元网络
        
        参数:
        input_size: 输入特征的维度
        """
        # 初始化权重和偏置
        self.weights = np.random.randn(input_size, 1) * 0.01
        self.bias = np.zeros((1, 1))
    
    def sigmoid(self, z):
        """
        sigmoid激活函数
        
        参数:
        z: 线性组合结果
        
        返回:
        激活后的值
        """
        return 1 / (1 + np.exp(-z))
    
    def sigmoid_derivative(self, z):
        """
        sigmoid激活函数的导数
        
        参数:
        z: 线性组合结果
        
        返回:
        sigmoid的导数
        """
        return self.sigmoid(z) * (1 - self.sigmoid(z))
    
    def forward(self, X):
        """
        前向传播
        
        参数:
        X: 输入数据,形状为(m, n),其中m是样本数,n是特征数
        
        返回:
        预测输出
        """
        # 计算线性组合
        self.z = np.dot(X, self.weights) + self.bias
        # 应用激活函数
        self.a = self.sigmoid(self.z)
        return self.a
    
    def compute_loss(self, y, a):
        """
        计算损失
        
        参数:
        y: 真实标签
        a: 预测输出
        
        返回:
        损失值
        """
        m = y.shape[0]
        # 计算交叉熵损失
        loss = -np.mean(y * np.log(a) + (1 - y) * np.log(1 - a))
        return loss
    
    def backward(self, X, y, a):
        """
        反向传播
        
        参数:
        X: 输入数据
        y: 真实标签
        a: 预测输出
        
        返回:
        权重和偏置的梯度
        """
        m = X.shape[0]
        
        # 计算输出层的梯度
        dz = a - y
        # 计算权重的梯度
        dw = (1/m) * np.dot(X.T, dz)
        # 计算偏置的梯度
        db = (1/m) * np.sum(dz, axis=0, keepdims=True)
        
        return dw, db
    
    def update_parameters(self, dw, db, learning_rate):
        """
        更新参数
        
        参数:
        dw: 权重的梯度
        db: 偏置的梯度
        learning_rate: 学习率
        """
        self.weights -= learning_rate * dw
        self.bias -= learning_rate * db
    
    def train(self, X, y, epochs=1000, learning_rate=0.01, print_interval=100):
        """
        训练模型
        
        参数:
        X: 输入数据
        y: 真实标签
        epochs: 训练轮数
        learning_rate: 学习率
        print_interval: 打印间隔
        """
        losses = []
        
        for epoch in range(epochs):
            # 前向传播
            a = self.forward(X)
            
            # 计算损失
            loss = self.compute_loss(y, a)
            losses.append(loss)
            
            # 反向传播
            dw, db = self.backward(X, y, a)
            
            # 更新参数
            self.update_parameters(dw, db, learning_rate)
            
            # 打印损失
            if epoch % print_interval == 0:
                print(f'Epoch {epoch}, Loss: {loss:.4f}')
        
        return losses
    
    def predict(self, X, threshold=0.5):
        """
        预测
        
        参数:
        X: 输入数据
        threshold: 阈值
        
        返回:
        预测标签
        """
        a = self.forward(X)
        return (a > threshold).astype(int)
    
    def evaluate(self, X, y):
        """
        评估模型
        
        参数:
        X: 输入数据
        y: 真实标签
        
        返回:
        准确率
        """
        predictions = self.predict(X)
        accuracy = np.mean(predictions == y)
        return accuracy

3.4 训练单神经元网络

现在,我们使用准备好的数据训练单神经元网络:

# 创建单神经元网络
neuron = SingleNeuron(input_size=2)

# 训练模型
losses = neuron.train(X, y, epochs=5000, learning_rate=0.1, print_interval=500)

# 可视化损失曲线
plt.plot(losses)
plt.title('Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

# 评估模型
accuracy = neuron.evaluate(X, y)
print(f'Training Accuracy: {accuracy:.4f}')

3.5 可视化决策边界

我们可以可视化单神经元网络的决策边界:

# 可视化决策边界
def plot_decision_boundary(model, X, y):
    # 设置网格范围
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    
    # 创建网格
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
                         np.linspace(y_min, y_max, 100))
    
    # 预测网格点的标签
    grid = np.c_[xx.ravel(), yy.ravel()]
    predictions = model.predict(grid)
    predictions = predictions.reshape(xx.shape)
    
    # 绘制决策边界
    plt.contourf(xx, yy, predictions, alpha=0.8)
    
    # 绘制数据点
    plt.scatter(X[y.flatten() == 0][:, 0], X[y.flatten() == 0][:, 1], color='blue', label='Class 0')
    plt.scatter(X[y.flatten() == 1][:, 0], X[y.flatten() == 1][:, 1], color='red', label='Class 1')
    
    plt.title('Decision Boundary')
    plt.xlabel('Feature 1')
    plt.ylabel('Feature 2')
    plt.legend()
    plt.show()

# 绘制决策边界
plot_decision_boundary(neuron, X, y)

3.6 分析训练结果

通过训练单神经元网络,我们可以:

  • 观察损失函数的下降过程
  • 了解模型的收敛情况
  • 可视化决策边界,理解模型的学习能力
  • 评估模型的准确率

4. 单神经元网络的局限性

4.1 线性可分问题

单神经元网络只能解决线性可分的二分类问题,对于非线性可分的问题,需要使用更复杂的神经网络模型。

4.2 解决方法

对于非线性可分的问题,可以:

  • 使用多层神经网络
  • 使用更复杂的激活函数
  • 对输入特征进行非线性变换

5. 扩展:使用不同的激活函数

5.1 常见的激活函数

除了sigmoid激活函数外,常见的激活函数还包括:

  • ReLU:f(z) = max(0, z)
  • tanh:f(z) = (e^z - e^-z) / (e^z + e^-z)
  • Leaky ReLU:f(z) = max(αz, z),其中α是一个小的正数

5.2 实现使用ReLU激活函数的单神经元网络

class SingleNeuronReLU:
    def __init__(self, input_size):
        """
        初始化单神经元网络
        
        参数:
        input_size: 输入特征的维度
        """
        # 初始化权重和偏置
        self.weights = np.random.randn(input_size, 1) * 0.01
        self.bias = np.zeros((1, 1))
    
    def relu(self, z):
        """
        ReLU激活函数
        
        参数:
        z: 线性组合结果
        
        返回:
        激活后的值
        """
        return np.maximum(0, z)
    
    def relu_derivative(self, z):
        """
        ReLU激活函数的导数
        
        参数:
        z: 线性组合结果
        
        返回:
        ReLU的导数
        """
        return np.where(z > 0, 1, 0)
    
    def forward(self, X):
        """
        前向传播
        
        参数:
        X: 输入数据,形状为(m, n),其中m是样本数,n是特征数
        
        返回:
        预测输出
        """
        # 计算线性组合
        self.z = np.dot(X, self.weights) + self.bias
        # 应用激活函数
        self.a = self.relu(self.z)
        return self.a
    
    def compute_loss(self, y, a):
        """
        计算损失
        
        参数:
        y: 真实标签
        a: 预测输出
        
        返回:
        损失值
        """
        m = y.shape[0]
        # 计算均方误差损失
        loss = (1/(2*m)) * np.sum((a - y)**2)
        return loss
    
    def backward(self, X, y, a):
        """
        反向传播
        
        参数:
        X: 输入数据
        y: 真实标签
        a: 预测输出
        
        返回:
        权重和偏置的梯度
        """
        m = X.shape[0]
        
        # 计算输出层的梯度
        dz = (a - y) * self.relu_derivative(self.z)
        # 计算权重的梯度
        dw = (1/m) * np.dot(X.T, dz)
        # 计算偏置的梯度
        db = (1/m) * np.sum(dz, axis=0, keepdims=True)
        
        return dw, db
    
    def update_parameters(self, dw, db, learning_rate):
        """
        更新参数
        
        参数:
        dw: 权重的梯度
        db: 偏置的梯度
        learning_rate: 学习率
        """
        self.weights -= learning_rate * dw
        self.bias -= learning_rate * db
    
    def train(self, X, y, epochs=1000, learning_rate=0.01, print_interval=100):
        """
        训练模型
        
        参数:
        X: 输入数据
        y: 真实标签
        epochs: 训练轮数
        learning_rate: 学习率
        print_interval: 打印间隔
        """
        losses = []
        
        for epoch in range(epochs):
            # 前向传播
            a = self.forward(X)
            
            # 计算损失
            loss = self.compute_loss(y, a)
            losses.append(loss)
            
            # 反向传播
            dw, db = self.backward(X, y, a)
            
            # 更新参数
            self.update_parameters(dw, db, learning_rate)
            
            # 打印损失
            if epoch % print_interval == 0:
                print(f'Epoch {epoch}, Loss: {loss:.4f}')
        
        return losses
    
    def predict(self, X, threshold=0.5):
        """
        预测
        
        参数:
        X: 输入数据
        threshold: 阈值
        
        返回:
        预测标签
        """
        a = self.forward(X)
        return (a > threshold).astype(int)
    
    def evaluate(self, X, y):
        """
        评估模型
        
        参数:
        X: 输入数据
        y: 真实标签
        
        返回:
        准确率
        """
        predictions = self.predict(X)
        accuracy = np.mean(predictions == y)
        return accuracy

5.3 比较不同激活函数的效果

# 创建使用ReLU激活函数的单神经元网络
neuron_relu = SingleNeuronReLU(input_size=2)

# 训练模型
losses_relu = neuron_relu.train(X, y, epochs=5000, learning_rate=0.1, print_interval=500)

# 可视化损失曲线
plt.plot(losses, label='Sigmoid')
plt.plot(losses_relu, label='ReLU')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

# 评估模型
accuracy_relu = neuron_relu.evaluate(X, y)
print(f'Training Accuracy (ReLU): {accuracy_relu:.4f}')

# 可视化决策边界
plot_decision_boundary(neuron_relu, X, y)

6. 扩展:使用不同的优化算法

6.1 梯度下降法的变体

除了基本的梯度下降法外,还有一些变体可以提高训练效果:

  • 随机梯度下降法(SGD):每次使用一个样本进行更新
  • 小批量梯度下降法(Mini-batch SGD):每次使用一小批样本进行更新
  • 动量梯度下降法:考虑之前的梯度方向
  • Adam优化算法:结合动量梯度下降法和RMSProp算法

6.2 实现小批量梯度下降法

def create_minibatches(X, y, batch_size):
    """
    创建小批量数据
    
    参数:
    X: 输入数据
    y: 真实标签
    batch_size: 批量大小
    
    返回:
    小批量数据的列表
    """
    m = X.shape[0]
    minibatches = []
    
    # 打乱数据
    permutation = np.random.permutation(m)
    X_shuffled = X[permutation]
    y_shuffled = y[permutation]
    
    # 创建小批量
    for i in range(0, m, batch_size):
        end = i + batch_size
        if end > m:
            end = m
        minibatch_X = X_shuffled[i:end]
        minibatch_y = y_shuffled[i:end]
        minibatches.append((minibatch_X, minibatch_y))
    
    return minibatches

# 修改train方法,使用小批量梯度下降法
def train_minibatch(self, X, y, epochs=1000, batch_size=32, learning_rate=0.01, print_interval=100):
    """
    使用小批量梯度下降法训练模型
    
    参数:
    X: 输入数据
    y: 真实标签
    epochs: 训练轮数
    batch_size: 批量大小
    learning_rate: 学习率
    print_interval: 打印间隔
    """
    losses = []
    m = X.shape[0]
    
    for epoch in range(epochs):
        epoch_loss = 0
        
        # 创建小批量
        minibatches = create_minibatches(X, y, batch_size)
        
        for minibatch_X, minibatch_y in minibatches:
            # 前向传播
            a = self.forward(minibatch_X)
            
            # 计算损失
            loss = self.compute_loss(minibatch_y, a)
            epoch_loss += loss * len(minibatch_X) / m
            
            # 反向传播
            dw, db = self.backward(minibatch_X, minibatch_y, a)
            
            # 更新参数
            self.update_parameters(dw, db, learning_rate)
        
        losses.append(epoch_loss)
        
        # 打印损失
        if epoch % print_interval == 0:
            print(f'Epoch {epoch}, Loss: {epoch_loss:.4f}')
    
    return losses

# 添加train_minibatch方法到SingleNeuron类
SingleNeuron.train_minibatch = train_minibatch

# 使用小批量梯度下降法训练模型
neuron_minibatch = SingleNeuron(input_size=2)
losses_minibatch = neuron_minibatch.train_minibatch(X, y, epochs=1000, batch_size=32, learning_rate=0.1, print_interval=100)

# 可视化损失曲线
plt.plot(losses, label='Full Batch')
plt.plot(losses_minibatch, label='Mini-batch')
plt.title('Training Loss Comparison')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

# 评估模型
accuracy_minibatch = neuron_minibatch.evaluate(X, y)
print(f'Training Accuracy (Mini-batch): {accuracy_minibatch:.4f}')

7. 总结与展望

7.1 主要内容总结

本教程介绍了如何实现一个单神经元网络,包括:

  • 单神经元网络的基本概念和结构
  • 单神经元网络的实现步骤
  • 实战案例:实现单神经元网络解决二分类问题
  • 单神经元网络的局限性
  • 扩展:使用不同的激活函数
  • 扩展:使用不同的优化算法

7.2 未来学习方向

通过本教程的学习,读者可以:

  • 掌握神经网络的基本实现方法
  • 理解前向传播和反向传播的原理
  • 了解不同激活函数和优化算法的效果
  • 为实现更复杂的神经网络模型打下基础

7.3 实践建议

在实践中,读者可以:

  • 尝试使用不同的数据集训练单神经元网络
  • 调整学习率、批量大小等超参数,观察其对训练效果的影响
  • 尝试实现其他类型的激活函数和优化算法
  • 逐步扩展到更复杂的神经网络模型,如多层感知器

通过不断的实践和探索,读者将能够更深入地理解神经网络的工作原理,为后续的深度学习学习打下坚实的基础。

« 上一篇 计算图与链式求导法则 下一篇 » 作业实战:实现浅层神经网络