深度学习-逻辑回归-单个神经元

发布于:2025-07-06 ⋅ 阅读:(13) ⋅ 点赞:(0)

在这里插入图片描述

逻辑回归的目的

逻辑回归是解决二分问题,判断样本属于正类的概率是多大,0-1之间

找到一组最佳的权重(w1,w2,w3,…) ,b,使得模型预测的概率 P(Y=1) 尽可能接近样本的真实标签(1 或 0)。

计算过程

前向传播过程如下:

假设有一个简单的神经网络层,包括输入 ( x )、权重 ( w )、偏置 ( b ) 和激活函数( σ),输出为 ( a )。损失函数为 ( L ),我们希望计算损失函数对权重 ( w ) 的梯度。

输入特征x
线性变换:z=w*x+b
激活函数:a=σ(z)
输出预测概率a

σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+ez1

损失函数,也就是误差

损失计算:L=Loss(a,y),其中 y 是真实标签,a是预测值

L ( y ^ , y ) = − y log ⁡ ( y ^ ) − ( 1 − y ) log ⁡ ( 1 − y ^ ) L(\hat{y}, y) = - y \log(\hat{y}) - (1 - y) \log(1 - \hat{y}) L(y^,y)=ylog(y^)(1y)log(1y^)

最常见的损失函数就是最小二乘法损失函数:1/2∑(目标值 - 预测值)²。即E_{total}=\frac{1}{2}(T-Y)^{2}。通过最小化均方误差,可以求解出最优的模型参数W,使得模型的预测结果Y与目标值之间T的平方差最小化。这样的损失函数对异常值比较敏感,因为它会对差值进行平方,导致异常值的差异被放大。

训练的目的是把L(y^,y)趋近0,让损失最小,就是趋向于最低点
其有w与b两个变量
在这里插入图片描述

反向传播,更新权重W

在这里插入图片描述
这里的 α是学习率,是调整的幅度,w的优化就是通过减去w的偏导数来达成的

J和L是一样的含义,都是损失函数

偏导数如何求(计算梯度)

反向传播中的梯度计算

  1. 计算损失函数 L L L 对激活值 a a a 的梯度:
    ∂ L ∂ a \frac{\partial L}{\partial a} aL
    在这里插入图片描述

  2. 计算激活函数 σ \sigma σ z z z 的梯度:
    ∂ a ∂ z = σ ′ ( z ) \frac{\partial a}{\partial z} = \sigma'(z) za=σ(z)
    在这里插入图片描述

  3. 计算 z z z 对权重 w w w 的梯度:
    ∂ z ∂ w = x \frac{\partial z}{\partial w} = x wz=x
    就是w

  4. 使用链式法则计算损失函数 L L L 对权重 w w w 的梯度:
    ∂ L ∂ w = ∂ L ∂ a ⋅ ∂ a ∂ z ⋅ ∂ z ∂ w \frac{\partial L}{\partial w} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial w} wL=aLzawz
    4中就是w关于L的偏导数

这是L
L ( y ^ , y ) = − y log ⁡ ( y ^ ) − ( 1 − y ) log ⁡ ( 1 − y ^ ) L(\hat{y}, y) = - y \log(\hat{y}) - (1 - y) \log(1 - \hat{y}) L(y^,y)=ylog(y^)(1y)log(1y^)
这是a
σ ( z ) = 1 1 + e − z \sigma(z) = \frac{1}{1 + e^{-z}} σ(z)=1+ez1
这是z
z = w ∗ x + b z=w*x+b z=wx+b
问题:计算出 J J J 关于 z z z 的导数

  • ∘   d J d z = d J d a ∗ d a d z \circ\ \dfrac{dJ}{dz}=\dfrac{dJ}{da}*\dfrac{da}{dz}  dzdJ=dadJdzda
  • ∘   d J d a = − y a + 1 − y 1 − a \circ\ \dfrac{dJ}{da} = -\dfrac{y}{a} + \dfrac{1 - y}{1 - a}  dadJ=ay+1a1y
  • ∘   d a d z = a ( 1 − a ) \circ\ \dfrac{da}{dz} = a(1 - a)  dzda=a(1a)

d z = d J d a ⋅ d a d z = a − y dz = \frac{dJ}{da} \cdot \frac{da}{dz} = a - y dz=dadJdzda=ay
d w 1 = d z d w 1 = ( a − y ) ⋅ x 1 dw_1 = \frac{dz}{dw_1} = (a - y) \cdot x_1 dw1=dw1dz=(ay)x1
d w 2 = d z d w 2 = ( a − y ) ⋅ x 2 dw_2 = \frac{dz}{dw_2} = (a - y) \cdot x_2 dw2=dw2dz=(ay)x2
d b = d z db = dz db=dz

在神经网络中,偏置项 b 是​​全局共享参数​​:,偏置项 b 影响所有样本的输出

计算梯度代码现

import numpy as np

# 激活函数及其导数
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
#倒数
def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

# 假设数据
x = np.array([0.5])  # 输入
w = np.array([0.2])  # 权重
b = 0.1  # 偏置
y = np.array([0.7])  # 实际目标

# 前向传播
z = w * x + b
a = sigmoid(z)

# 计算损失(均方误差)
loss = 0.5 * (a - y) ** 2
print(f'损失对权重的梯度: {loss}')

# 反向传播(链式法则)
dL_da = a - y  # 损失函数对激活值的梯度
da_dz = sigmoid_derivative(z)  # 激活函数对 z 的梯度
dz_dw = x  # z 对权重 w 的梯度

# 使用链式法则计算损失函数对权重的梯度
dL_dw = dL_da * da_dz * dz_dw

print(f'损失对权重的梯度: {dL_dw}')
#这个就是J对w的梯度,就是那个偏导数

完整代码实现

数学原理

  • 正向传播
    初始化,假设n个特征,m个样本
    J = 0 J=0 J=0
    W = n p . z e r o s ( [ n , 1 ] ) W=np.zeros([n,1]) W=np.zeros([n,1])
    b = 0 b=0 b=0
    Z = n p . d o t ( W T , X ) + b Z=np.dot(W^{T},X)+b Z=np.dot(WT,X)+b
    A = σ ( Z ) A=\sigma(Z) A=σ(Z)

  • 反向传播
    每个样本梯度计算过程为:
    d Z = A − Y dZ = A - Y dZ=AY
    d W = 1 m X d Z T dW = \frac{1}{m} XdZ^{T} dW=m1XdZT
    d b = 1 m np.sum ( d Z ) db = \frac{1}{m} \text{np.sum}(dZ) db=m1np.sum(dZ)

更新
W : = W − α d W W := W - \alpha dW W:=WαdW
b : = b − α d b b := b - \alpha db b:=bαdb

  • 各个函数
import numpy as np
#激活函数sigmoid()
def basic_sigmoid(x):
    s = 1 / (1 + np.exp(-x))
    return s

#初始化W,b
def initialize_weights(shape):
    w=np.zeros((shape,1))
    b=0
    #这里w全为1,后面会迭代,1只是初始值
    assert(w.shape == (shape,1))
    assert(isinstance(b,float) or isinstance(b,int))
    #assert检测类型是否正确,相当于异常检测
    return w,b

#前向传播与反向传播,propagate传播
#用于得到 平均损失cost,dW均值,与db,便于后续优化optimize
def propagate(W,b,X,Y):
    #W权重(n,1),b是偏置向量,X是输入的m个样本的(n,m)矩阵,Y是实际值用于反向传播

    m=X.shape[1]  #获取X的列数,即样本的个数
    #前向传播
    #np.dot()是矩阵乘法,W,T大写代表矩阵,+b是矩阵结果的每一项都+b
    #得到的A是(1,m)的一维矩阵
    A=basic_sigmoid(np.dot(W.T,X)+b)

    #计算损失
    #操作对象是矩阵,想达到向量化运算,用np
    cost=-1/m*np.sum(np.log(A)+(1-Y)*np.log(1-A)) #求损失平均值,负的(-),越接近0正确率越高

    ##反向传播
    #下面得到的是dW是w得平均值(n,1)矩阵
    dz=A-Y   #得到(1,m)
    dW=1/m*np.dot(X,dz.T)  #n*m M*1->(n,1)
    db=1/m*np.sum(dz)      #偏置向量怎么调?

    assert(dW.shape ==W.shape)
    assert(db.dtype == float)
    #np.squeeze(cost)确保cost是一个标量(不是矩阵)
    cost=np.squeeze(cost)
    assert(cost.shape == ())
    grads={
        "dW":dW,
        "db":db
    }
    return grads,cost

#优化
def optimize(W,b,X,Y,num_iterations,learning_rate):
    '''
参数
 w:权重;b:偏置;x:特征; y:目标值;num_iterations:总迭代次数;learning_rate:学习率
Returns:
params:更新后的参数字典; grads:梯度 ; costs:损失结果
    '''
    costs=[] #用于存放损失的变化
    for i in range(num_iterations):
        grads,cost=propagate(W,b,X,Y)
        #取出dW的平均值梯度(n,1)矩阵,db
        dW=grads['dW']
        db=grads['db']

        #根据优化公式更新
        w=W-learning_rate*dW #权重
        b=b-learning_rate*db #偏置

        #记录cost的变化,每隔100个记录一次
        if i % 100==0:
            costs.append(cost)
            print("损失结果 %i,%f" %(i,cost))

        params={
            "w":w,
            "b":b
        }#更新之后的线性变换的参数
        grads={
            "dW":dW,
            "db":db
        }#没变,还是grads,cost=propagate(W,b,X,Y)
    return params,grads,costs

#预测  (权重,偏置,输入)
#返回的是一个(1,m)的矩阵
def predict(W , b, X):
    m=X.shape[1] #得到X列数,即样本个数 X(n,m)
    Y_prediction=np.zeros(1,m) #定义一个存放预测值的(1,m)矩阵
    W=W.reshape(X.shape[0],1)        #定义一个(n,1)的矩阵,用于存放w1-wn的权重

    #预测
    A=basic_sigmoid(np.dot(W.T,X)+b)  #(1,m)的预测值

    for i in range(A.shape[1]): #循环m次
        if A[0,i]<=0.5:
            Y_prediction[0,i]=0
        if A[0,i]>=0.5:
            Y_prediction[0,i]=1

    assert(Y_prediction.shape==(1,m))

    return Y_prediction

#汇总函数
def model(X_train,Y_train,X_test,Y_test,num_iterations=2000,learning_rate=0.5):
    # 初始化W,b
    W,b=initialize_weights(X_train)

    #梯度下降优化
    #应该是这个吧X_train,Y_train
    params,grads,costs=optimize(W,b,X_train,Y_train,num_iterations,learning_rate)

    W=params['W']
    b=params['b']
    Y_prediction_train=predict(W,b,X_train)
    Y_prediction_test=predict(W,b,X_test)

    #打印准确率
    print("训练集准确率:{}".format(100-np.mean(np.abs(Y_prediction_train-Y_train))*100))
    print("测试集准确率:{}".format(100-np.mean(np.abs(Y_prediction_test-Y_test))*100))

    #数据汇总
    d={
        "costs":costs,
        "Y_prediction_train":Y_prediction_train,
        "Y_prediction_test":Y_prediction_test,
        "W":W,
        "b":b,
        "learning_rate":learning_rate,
        "num_iterations":num_iterations
    }
    return d

if __name__=="__main__":
    # 最后运行模型
    model_result = model(X_train, Y_train, X_test, Y_test)
    print(model_result)
  • 总体实现 单神经元
import numpy as np

# 训练集
X_train = np.array([
    [0.1, 0.3, 0.5, 0.7, 0.9, 0.2, 0.4, 0.6],  # 特征1
    [1.2, 0.8, 1.0, 1.5, 0.5, 1.1, 0.9, 1.3]  # 特征2
])  # (2, 8) 特征矩阵,2个特征,8个样本

Y_train = np.array([
    [0, 0, 0, 1, 1, 0, 0, 1]
])  # (1, 8) 标签向量

# 测试集
X_test = np.array([
    [0.3, 0.8, 0.2, 0.5],  # 特征1
    [1.0, 0.7, 1.1, 0.9]  # 特征2
])  # (2, 4) 特征矩阵,2个特征,4个样本

Y_test = np.array([
    [0, 1, 0, 1]
])  # (1, 4) 标签向量


# 激活函数sigmoid()
def basic_sigmoid(x):
    s = 1 / (1 + np.exp(-x))
    return s


# 初始化W,b
def initialize_weights(shape):
    w = np.zeros((shape, 1))
    b = 0
    assert w.shape == (shape, 1)
    assert isinstance(b, float) or isinstance(b, int)
    return w, b


# 前向传播与反向传播
def propagate(W, b, X, Y):
    m = X.shape[1]  # 样本个数

    # 前向传播 (添加了数值稳定性)
    A = basic_sigmoid(np.dot(W.T, X) + b)

    # 计算损失 (避免log(0))
    epsilon = 1e-5
    cost = -1 / m * np.sum(Y * np.log(A + epsilon) + (1 - Y) * np.log(1 - A + epsilon))

    # 反向传播
    dz = A - Y
    dW = 1 / m * np.dot(X, dz.T)
    db = 1 / m * np.sum(dz)

    # 验证
    assert dW.shape == W.shape
    assert isinstance(db, float)
    cost = np.squeeze(cost)
    assert cost.shape == ()

    grads = {"dW": dW, "db": db}
    return grads, cost


# 优化
def optimize(W, b, X, Y, num_iterations, learning_rate):
    costs = []

    # 复制参数避免修改原始值
    W = W.copy()
    b = b

    for i in range(num_iterations):
        grads, cost = propagate(W, b, X, Y)
        dW = grads["dW"]
        db = grads["db"]

        # 更新参数
        W = W - learning_rate * dW
        b = b - learning_rate * db

        # 每100次记录一次损失
        if i % 100 == 0:
            costs.append(cost)
            print(f"迭代 {i}, 损失: {cost:.6f}")

    # 记录最终损失
    _, final_cost = propagate(W, b, X, Y)
    costs.append(final_cost)
    print(f"最终迭代 {num_iterations}, 损失: {final_cost:.6f}")

    params = {"W": W, "b": b}
    grads = {"dW": dW, "db": db}
    return params, grads, costs


# 预测
def predict(W, b, X):
    m = X.shape[1]
    Y_prediction = np.zeros((1, m))  # 修正这里:np.zeros((1, m))

    # 确保W是列向量
    if len(W.shape) == 1:
        W = W.reshape(-1, 1)

    # 计算预测值
    A = basic_sigmoid(np.dot(W.T, X) + b)

    # 二分预测
    Y_prediction = (A > 0.5).astype(int)

    assert Y_prediction.shape == (1, m)
    return Y_prediction


# 模型汇总
def model(X_train, Y_train, X_test, Y_test, num_iterations=2000, learning_rate=0.5):
    # 初始化
    n_features = X_train.shape[0]
    W, b = initialize_weights(n_features)

    # 优化
    params, grads, costs = optimize(W, b, X_train, Y_train, num_iterations, learning_rate)

    # 获取参数
    W = params["W"]
    b = params["b"]

    # 预测
    Y_prediction_train = predict(W, b, X_train)
    Y_prediction_test = predict(W, b, X_test)

    # 计算准确率
    train_accuracy = 100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100
    test_accuracy = 100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100

    print("\n模型训练结果:")
    print(f"训练集准确率: {train_accuracy:.2f}%")
    print(f"测试集准确率: {test_accuracy:.2f}%")

    # 结果汇总
    result = {
        "costs": costs,
        "Y_prediction_train": Y_prediction_train,
        "Y_prediction_test": Y_prediction_test,
        "W": W,
        "b": b,
        "learning_rate": learning_rate,
        "num_iterations": num_iterations,
        "train_accuracy": train_accuracy,
        "test_accuracy": test_accuracy
    }
    return result


if __name__ == "__main__":
    # 运行模型
    model_result = model(
        X_train=X_train,
        Y_train=Y_train,
        X_test=X_test,
        Y_test=Y_test,
        num_iterations=2000,
        learning_rate=0.05
    )

    print("\n最终参数:")
    print(f"权重 W: \n{model_result['W']}")
    print(f"偏置 b: {model_result['b']:.4f}")


总的来说

逻辑回归只会给出 0 1的判断,而我们优化的就是减小这个0 1判断的误差