用单个神经元实现TensorFlow线性回归拟合

        很多介绍TF开发的书籍中都喜欢用逻辑回归拟合线性二维数据来开始介绍TF的开发过程,按照数据准备,模型搭建,反向损失函数定义和训练模型,使用模型的的顺序来介绍,并给出代码。但TF框架本身隐藏了其中的大部分流程,只暴露了一小部分参数供给用户程序调节,导致学习者知其然但不知道其所以然,可能一行剪短的tf函数调用,隐藏了大部分的实现细节。根据网上学习的资料,结合自己摸索,这里给出一种不依赖tensorflow用单神经元实现一次函数拟合的例子,也是用python实现。这样做一方面是因为自己也是个新手,写出来的过程本身就是学习的过程,可以加深印象,另一方面,如果当中有不对的地方,也好让别人指点,纠正。

单个神经元工作模型:

 对于符合形如

    

 此类线性函数分布的数据,理论上单个神经元即可实现对其进行拟合. 单神经元的工作模型如下:

    

网络完成的功能简要介绍如下,x作为样本输入端,w是权重,中间方形框表示x,w之间的操作是乘法操作,然后结果在加上后续的b,得到的结果y在进行一个叫做activate function函数的变换,得到最终输出o. l上述计算过程用方程表达如下:

                    

在神经网络领域,专业的叫法称呼b为偏置,w为权重,f为激活函数(activate function),也叫非线性单元

拟合的过程不过就是通过不断的输入样本,根据输出值和预期值的误差变化趋势,不断调整权重w和偏置b,得到适合样本数据的最佳权重和置换值,这个过程就是训练的过程。

另外插一句题外话,我学的是自动化,自动化专业接触最多的一个词就是”负反馈",上面介绍的神经网络工作过程,实际上也是用了反馈的原理,将预期值和实际值之间的误差作为控制信号源,通过网络”反馈“给前面的神经元节点,产生控制w和b变化的控制信号,从而使输出和预期值拟合的过程。常用的BP神经网络,BP(back propagate,负向传播)也和负反馈的原理神合,负反馈对应的控制变量变成了负梯度。

下面根据以上网络模型,给出带噪声的符合y=6x规律的数据拟合实现。

数据准备:

 下面的代码产生一组介于区间[-1,1]之间的100个样点,并将每个样点值乘以6之后加上随机噪声进行输出。

import numpy as np
import matplotlib.pyplot as plt


traing_x=np.linspace(-1, 1, 100)
traing_y=6*traing_x + np.random.randn(*traing_x.shape) * 0.3

plt.plot(traing_x,traing_y,'ro', label='Original data')
plt.legend()
plt.show()

数据图像为:

    

根据数据图像分布来看,总体上符合

         

的分布规律,噪声影响导致数据点在距离直线不远的范围浮动变化。

选择激活函数:

 选择比较常用的sigmoid函数作为激活函数,其图像为:

选择将其作为激活函数的另一个原因是, 从数据分布图上可见,在区间 x的取值区间[-1,1],y 的取值范围为[-6,6], sigmoid函数在[-6,6]区间上具备比较良好的区分度。

将函数复核后,上述神经网络的传递方程式:

图像变成如下所示:

网络traing是根据负梯度进行的,负梯度的计算过程是损失函数对权重和偏置的偏导数,根据链式求导法则,对sigmoid函数求导是链式法则的中间一环,下面是计算sigmoid导数的过程

所以,sigmoid函数的导数为:

选择损失函数:

 这里选择均值平方差(Mean Squared Error, MSE),也叫做”均方误差" 作为训练结果的评价指标,也就是loss函数,它主要表达的是预测值和真实值之间的差异,在数理统计中,均方误差是指参数估计值与参数真实值之差平方的期望值,它的定义如下:

                        

其中预测值和真实值是经过sigmoid函数进行映射之后的输出。

 以单次训练为例,本例中,每一期训练的每单笔数据均为一个样本:

                                  

训练优化的目的就是不断减少损失函数的值,我们知道,改变网络的权重和偏置可以影响预测值,但我们应该怎么做才能使结果朝着损失函数减少的方向优化呢?这里需要高等数学中的一些概念。

observde是样本真值,是属于模型外部输入的常数,不用对其求导,所以,对w, b分别求偏导为:

模型搭建:

    定义激活函数, 损失函数,sigmoid求导函数等子程序:

# Sigmoid activation function: f(x) = 1 / (1 + e^(-x))
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Derivative of sigmoid: f'(x) = f(x) * (1 - f(x))    
def derivative_sigmoid(x):
    f = sigmoid(x)
    return f * (1 - f)

def mse_loss(expect, actual):
    return ((expect - actual) ** 2).mean()

然后定义神经元,其中导数按照上面给出的公式计算,但是,根据偏导数更新新的w,b值呢? 这里用上了负梯度的概念。

根据上面的公式,损失函数的梯度为:

至于为什么选择负梯度,你可以这样想:

其中\eta为梯度下降算法的下降变化率,用来控制梯度算法的收敛速度的,在机器学习中有一个专门的名字,叫做学习率,可以取0~1之间的数字,当得到在局部最优的偏置和权重后,梯度向量变为0,递推公式得到稳定输出,此时

梯度前加一个负号,就意味着朝着梯度相反的方向前进!梯度的方向实际就是函数在此点上升最快的方向,而我们需要朝着下降最快的方向走,自然就是负的梯度的方向,所以此处需要加上负号;那么如果时上坡,也就是梯度上升算法,当然就不需要添加负号了,这也梯度下降方法,下降一词的由来。

算法达到最优解.

class one_neural_network:
  def __init__(self):
    # Weights
    self.w = np.random.normal()
    # Biases
    self.b = np.random.normal()
 

  def feedforward(self, x):
    # x is a numpy array with 2 elements.
    o= sigmoid(self.w * x + self.b)
    return o
    
  def train(self, input, expect):
    learn_rate = 0.1
    epochs = 600 # number of times to loop through the entire dataset
    datasets = traing_x.shape[0];
    
    result = np.zeros(epochs)
    for epoch in range(epochs):
      for counter in range(datasets):
        x=traing_x[counter]
        y=traing_y[counter]
        
        o1 = self.w * x + self.b
        o2 = sigmoid(o1)
        
        exp = sigmoid(y)      
        d_L_d_input = -2 * (exp - o2)
        d_input_d_w = x * derivative_sigmoid(o1)
        d_input_d_b = derivative_sigmoid(o1)
        
        self.w -= learn_rate * d_L_d_input * d_input_d_w
        self.b -= learn_rate * d_L_d_input * d_input_d_b

      y_predict = np.apply_along_axis(self.feedforward, 0, traing_x)
      loss = mse_loss(sigmoid(traing_y), y_predict)
      print("Epoch %d loss: %.3f" % (epoch, loss))
      result[epoch] = loss;
    print(self.w)
    print(self.b)
    plt.plot(result)
    plt.grid(True)
    plt.axis('tight')
    #plt.ylim(0,0.1)
    plt.show()

编写应用代码:  

   应用代码其实非常简单,都是对上面已经封装好的函数的直接调用,把模型数据喂给这些函数就好了.

# train our neural network!
network = one_neural_network()
network.train(traing_x, traing_y)

执行训练:

学习率是超参数,它并非由训练得到,而是由部署者的经验决定,程序中设置学习率为0.1,训练600轮,经过600轮的训练后,得到的结果如下:

Epoch 595 loss: 0.001
Epoch 596 loss: 0.001
Epoch 597 loss: 0.001
Epoch 598 loss: 0.001
Epoch 599 loss: 0.001
6.02222669476
0.0489193518788

损失曲线:

由数据可见,经过600轮训练后,loss平均值稳定再0.001,从损失曲线也可以明显看出,随着训练进行,MSE损失越来越小。

最终,训练出来的w=6.02222669476,b=0.0489193518788非常接近真实值6和0.

进一步,修改模型函数,调整初始数据的w和b值,看上述代码能否追踪新的变化,让b=4, w=2,调整训练1500轮。

traing_y=2*traing_x + np.random.randn(*traing_x.shape) * 0.3+4

损失函数变化图:


Epoch 1496 loss: 0.000
Epoch 1497 loss: 0.000
Epoch 1498 loss: 0.000
Epoch 1499 loss: 0.000
1.93756984637
3.91283159652

值得注意的是学习率的值对训练效果影响很大,不合适的学习率甚至有可能导致损失函数不收敛,在合适的学习率下,损失函数每一步迭代都会有所减小,表现在图像中就是损失函数随着迭代次数增加,呈现单调递减的形态。

数学家已经证明,只要学习率设置的足够小,损失函数一定会收敛,只是收敛的速度有快有慢,所以如果遇到不收敛的情况,最大可能是学习率设置的太大了。

可以看到w收敛到1.93756984637,b收敛到3.91283159652,非常接近2和4,loss值已经接近0,误差基本检测不到了,训练中由于每个训练样本执行完后都会更新权重和偏置,所以batch size为1,也可以设置其它的batch size,等多笔样本训练完成后,再对每个训练样本产生的梯度求和,算出总梯度更新权重和偏置。

以上就是TF框架中关于回归训练的原理展开,虽然不一定相同,但思想应该是一致的。

附图

执行完学习,就该进行推理了,输入一个新的x值,它会给你一个最符合实际的输出。全连接神经网络是一个通用的近似框架,只要有足够多的神经元,即使只有一层隐藏层的神经网络,利用常用的sigmoid,relu等激活函数,就可以无限逼近任何连续函数。在实际中,如果想使用浅层神经网络来拟合复杂非线性函数,就需要靠增加神经元的个数来实现,神经元过多意味着需要训练的参数过多,这会增加网络的学习难度,并影响网络的泛化能力,因此,在搭建网络结构是,一般倾向于使用更深的模型,来减少网络中需要的神经元数量,使网络有更好的泛化能力。


完整代码:

# -*- coding: utf-8 -*-
"""
Created on Tue Nov  3 21:52:30 2020

@author: caozilong
"""

import numpy as np
import matplotlib.pyplot as plt

saver=tf.train.Saver()


traing_x=np.linspace(-1, 1, 100)
traing_y=2*traing_x + np.random.randn(*traing_x.shape) * 0.3+4

plt.plot(traing_x,traing_y,'ro', label='Original data')
plt.legend()
plt.show()

# Sigmoid activation function: f(x) = 1 / (1 + e^(-x))
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Derivative of sigmoid: f'(x) = f(x) * (1 - f(x))    
def derivative_sigmoid(x):
    f = sigmoid(x)
    return f * (1 - f)

def mse_loss(expect, actual):
    return ((expect - actual) ** 2).mean()
    
'''
A neural network with:
  - 1 input
  - no hidden layer
  - 1 output

*** DISCLAIMER ***:
  The code below is intended to be simple and educational, NOT optimal.
  Real neural net code looks nothing like this. DO NOT use this code.
  Instead, read/run it to understand how this specific network works.
'''    
class one_neural_network:
  def __init__(self):
    # Weights
    self.w = np.random.normal()
    # Biases
    self.b = np.random.normal()
 

  def feedforward(self, x):
    # x is a numpy array with 2 elements.
    o= sigmoid(self.w * x + self.b)
    return o
    
  def train(self, input, expect):
    learn_rate = 0.1
    epochs = 1500 # number of times to loop through the entire dataset
    datasets = traing_x.shape[0];
    
    result = np.zeros(epochs)
    for epoch in range(epochs):
      for counter in range(datasets):
        x=traing_x[counter]
        y=traing_y[counter]
        
        o1 = self.w * x + self.b
        o2 = sigmoid(o1)
        

        exp = sigmoid(y)      
        d_L_d_input = -2 * (exp - o2)
        d_input_d_w = x * derivative_sigmoid(o1)
        d_input_d_b = derivative_sigmoid(o1)
        
        self.w -= learn_rate * d_L_d_input * d_input_d_w
        self.b -= learn_rate * d_L_d_input * d_input_d_b

      y_predict = np.apply_along_axis(self.feedforward, 0, traing_x)
      loss = mse_loss(sigmoid(traing_y), y_predict)
      print("Epoch %d loss: %.3f" % (epoch, loss))
      result[epoch] = loss;
    print(self.w)
    print(self.b)
    plt.plot(result)
    plt.grid(True)
    plt.axis('tight')
    #plt.ylim(0,0.1)
    plt.show()
          
# Train our neural network!
network = one_neural_network()
network.train(traing_x, traing_y)
    

结束

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
多元线性回归是指有多个自变量的线性回归模型,它可以用来预测因变量和多个自变量之间的关系。在本文中,我们将使用TensorFlow实现多元线性回归模型。 1. 数据准备 我们将使用波士顿房价数据集来演示多元线性回归。该数据集包含506个样本,每个样本有13个自变量和一个因变量,即波士顿地区房价的中位数。我们可以使用sklearn库中的load_boston函数来加载数据集。 ```python from sklearn.datasets import load_boston boston = load_boston() X = boston.data y = boston.target ``` 2. 数据预处理 在进行多元线性回归之前,我们需要对数据进行预处理。首先,我们将数据集拆分为训练集和测试集。我们将80%的数据用于训练,20%的数据用于测试。 ```python from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) ``` 接下来,我们需要对数据进行标准化处理,以便模型能够更好地拟合数据。我们可以使用sklearn库中的StandardScaler来标准化数据。 ```python from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) ``` 3. 模型构建 我们将使用TensorFlow来构建多元线性回归模型。我们首先需要定义模型的输入和输出。输入是一个包含13个特征的向量,输出是一个标量,表示房价的中位数。 ```python import tensorflow as tf inputs = tf.keras.layers.Input(shape=(X_train.shape[1],)) outputs = tf.keras.layers.Dense(1, activation=None)(inputs) model = tf.keras.models.Model(inputs=inputs, outputs=outputs) ``` 我们使用一个具有单个神经元的Dense层作为输出层。由于我们是进行回归任务,因此我们不需要使用激活函数。 4. 模型训练 在训练模型之前,我们需要定义损失函数和优化器。我们将使用均方误差作为损失函数,使用Adam优化器来更新模型参数。 ```python loss_fn = tf.keras.losses.MeanSquaredError() optimizer = tf.keras.optimizers.Adam(learning_rate=0.01) model.compile(optimizer=optimizer, loss=loss_fn) ``` 接下来,我们可以使用fit函数来训练模型。 ```python history = model.fit(X_train, y_train, epochs=100, validation_data=(X_test, y_test)) ``` 5. 模型评估 我们可以使用evaluate函数来评估模型在测试集上的性能。 ```python loss = model.evaluate(X_test, y_test) print("Mean squared error on test set:", loss) ``` 6. 模型预测 最后,我们可以使用predict函数来进行预测。 ```python y_pred = model.predict(X_test) # 可视化预测结果和真实值 import matplotlib.pyplot as plt plt.scatter(y_test, y_pred) plt.xlabel("True Values") plt.ylabel("Predictions") plt.show() ``` 以上就是使用TensorFlow实现多元线性回归的完整代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值