政安晨:【示例演绎】【用TensorFlow编写线性分类器】—— 同时了解一点TensorFlow与Keras的基本概念

环境准备

如果小伙伴们第一次接触TensorFlow与Keras,可以先看一下我的这篇文章做些环境准备(可以先忽略这篇文章里面代码实现部分,仅查看这里的环境准备部分即可)。

文章如下:

政安晨:【详细解析】【用TensorFlow从头实现】一个机器学习的神经网络小示例【解构演绎】icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136108510大家准备好环境后,咱们开始。


再熟悉一些概念

神经网络的训练一般都是围绕以下这些概念进行的:

低阶张量操作这是所有现代机器学习的底层架构,可以转化为TensorFlow API。

张量包括存储神经网络状态的特殊张量(变量)。

张量运算比如加法、relu、matmul。

反向传播一种计算数学表达式梯度的方法(在TensorFlow中通过GradientTape对象来实现)。

多层可以构成模型。

损失函数它定义了用于学习的反馈信号。

优化器它决定学习过程如何进行。

指标用于评估模型性能,比如精度等。

训练循环执行小批量梯度随机下降。

概念再配合代码:热热身

咱们接下来,再用概念配合一些代码做些小尝试。

常数张量和变量

要使用TensorFlow,我们需要用到一些张量,创建张量需要给定初始值

比如:可以创建全1张量或全0张量,也可以从随机分布中取值来创建张量。

为了进行接下来的演绎,咱们先导入tensorflow:

import tensorflow as tf

现在,咱们创建一个全1张量

# 等同于np.ones(shape=(2, 1)) 
x = tf.ones(shape=(2, 1))

执行如下:

同时,咱们再创建一个全0张量

#  等同于np.zeros(shape=(2, 1))
x = tf.zeros(shape=(2, 1))

执行如下:

接下来,咱们创建随机张量

# 从均值为0、标准差为1的正态分布中抽取的随机张量
# 等同于np.random.normal(size=(3, 1), loc=0., scale=1.)
x = tf.random.normal(shape=(3, 1), mean=0., stddev=1.)

执行:

咱们再创建一个随机张量:

# 从0和1之间的均匀分布中抽取的随机张量
# 等同于np.random.uniform(size=(3, 1), low=0., high=1.)
x = tf.random.uniform(shape=(3, 1), minval=0., maxval=1.)

执行:

NumPy数组和TensorFlow张量之间的一个重要区别是:TensorFlow张量是不可赋值的,它是常量。

要训练模型,我们需要更新其状态,而模型状态是一组张量:

如果张量不可赋值,那么我们该怎么做呢?这时就需要用到变量(variable),tf.Variable是一个类,其作用是管理TensorFlow中的可变状态。

要创建一个变量,你需要为其提供初始值,比如随机张量,如下所示:

v = tf.Variable(initial_value=tf.random.normal(shape=(3, 1)))
print(v)

执行:

变量的状态可以通过其assign方法进行修改,如下:

v.assign(tf.ones((3, 1)))

这种方法也适用于变量的子集,这种方法也适用于变量的子集:

v[0, 0].assign(3.)

执行:

可以看到,咱们已经将这个张量的一个元素给改掉了。

与此类似,assign_add()和assign_sub()分别等同于+=和-=的效果。

比如 assign_add() :

v.assign_add(tf.ones((3, 1)))

上面这段代码的意思是在现有v张量上增加一个全1的形状为(3,1)的张量。

用TensorFlow进行张量运算

就像NumPy一样,TensorFlow提供了许多张量运算来表达数学公式。

下面是一些基本的数学运算(大家可以自己尝试)

a = tf.ones((2, 2))

# 求平方

b = tf.square(a)

# 求平方根

c = tf.sqrt(a)

# 两个张量(逐元素)相加

d = b + c

# 计算两个张量的积

e = tf.matmul(a, b)

# 两个张量(逐元素)相乘

e *= d

上述代码中每个运算都是立即执行的(执行完就可以打印出结果)。

GradientTape API

虽然您可能会觉得TensorFlow看起来很像NumPy,但是NumPy无法做到的却是:

TensorFlow能够检索任意可微表达式相对于其输入的梯度。

使用TensorFlow,您只需要创建一个GradientTape作用域,对一个或多个输入张量做一些计算,然后就可以检索计算结果相对于输入的梯度。

代码如下:

input_var = tf.Variable(initial_value=3.)
with tf.GradientTape() as tape:
   result = tf.square(input_var)
gradient = tape.gradient(result, input_var)

到现在,您遇到的还只是tape.gradient()的输入张量是TensorFlow变量的情况,实际上,它的输入可以是任意张量,但在默认情况下只会监视可训练变量(trainable variable),如果要监视常数张量,那么必须对其调用tape.watch(),手动将其标记为被监视的张量。

对常数张量输入使用GradientTape:

input_const = tf.constant(3.)
with tf.GradientTape() as tape:
   tape.watch(input_const)
   result = tf.square(input_const)
gradient = tape.gradient(result, input_const)

之所以要监视数据,是因为如果预先存储计算梯度所需的全部信息,那么计算成本非常大。

为避免浪费资源,梯度带需要知道监视什么,它默认监视可训练变量,因为计算损失相对于可训练变量列表的梯度,是梯度带最常见的用途,梯度带是一个非常强大的工具,它甚至能够计算二阶梯度(梯度的梯度)

要理解二阶梯度,咱们举个例子

物体位置相对于时间的梯度这个物体的速度二阶梯度则是它的加速度

咱们再进行一个小例子:

测量一个垂直下落的苹果的位置随时间的变化,并且发现它满足position(time) = 4.9 * time ** 2,那么它的加速度是多少?

我们可以用两个嵌套的梯度带找出答案:

代码如下:

time = tf.Variable(0.)
with tf.GradientTape() as outer_tape:
    with tf.GradientTape() as inner_tape:
        position =  4.9 * time ** 2
    speed = inner_tape.gradient(position, time)

# 内梯度带计算出一个梯度,我们用外梯度带计算这个梯度的梯度。答案自然是4.9 * 2 = 9.8
acceleration = outer_tape.gradient(speed, time)

小伙伴们执行一下,可以看到结果:

热身完毕,咱们已经了解一些概念、完成了一些小尝试。

正式开始

咱们现在开始,用TensorFlow编写线性分类器

现在您已经了解了张量、变量和张量运算,也知道如何计算梯度啦,这些已经足以构建任意基于梯度下降的机器学习模型

首先,我们生成一些线性可分的数据二维平面上的点,它们分为两个类别。

生成方法从一个具有特定协方差矩阵和特定均值的随机分布中抽取坐标来生成每一类点

直观上来看协方差矩阵描述了点云的形状均值则描述了点云在平面上的位置我们设定两个点云的协方差矩阵相同,但均值不同,也就是说,两个点云具有相同的形状,但位置不同

现在有些小伙伴看不懂没关系,咱们跟着演绎,先走一遍。

现在,咱们在二维平面上随机生成两个类别的点

import numpy as np
num_samples_per_class = 1000

# (本行及以下3行)生成第一个类别的点:1000个二维随机点。协方差矩阵为[[1, 0.5], [0.5, 1]],对应于一个从左下方到右上方的椭圆形点云
negative_samples = np.random.multivariate_normal(
    mean=[0, 3],
    cov=[[1, 0.5],[0.5, 1]],
    size=num_samples_per_class)

# (本行及以下3行)生成第二个类别的点,协方差矩阵相同,均值不同
positive_samples = np.random.multivariate_normal(
    mean=[3, 0],
    cov=[[1, 0.5],[0.5, 1]],
    size=num_samples_per_class)

negative_samplespositive_samples都是形状为(1000, 2)的数组。

咱们现在将二者堆叠成一个形状为(2000, 2)的数组:

inputs = np.vstack((negative_samples, positive_samples)).astype(np.float32)

接下来,我们来生成对应的目标标签(即一个形状为(2000, 1)的数组)。

其元素都是0或1:

如果输入inputs[i]属于类别0,则目标targets[i,0]为0;

如果inputs[i]属于类别1,则targets[i, 0]为1。

下列代码生成对应的目标标签(0和1):

targets = np.vstack((np.zeros((num_samples_per_class, 1), dtype="float32"),
                     np.ones((num_samples_per_class, 1), dtype="float32")))

咱们下面用Matplotlib来绘制数据图像(绘制两个点类的图像)

import matplotlib.pyplot as plt
plt.scatter(inputs[:, 0], inputs[:, 1], c=targets[:, 0])
plt.show()

关于Matplotlib的用法,我以前有几篇文章,大家可以参考:

政安晨:在Jupyter中【示例演绎】Matplotlib的官方指南(一){Pyplot tutorial}icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136096870政安晨:在Jupyter中【示例演绎】Matplotlib的官方指南(二){Image tutorial}·{Python语言}icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136100806政安晨:在Jupyter中【示例演绎】Matplotlib的官方指南(三){Plot全工作流程展示}·{Python语言}icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136101342政安晨:在Jupyter中【示例演绎】Matplotlib的官方指南(四){Artist tutorial}·{Python语言}icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136102477

从上面的图可以看到,咱们的数据确实是二维平面上的两类随机点。

现在咱们来创建一个线性分类器,用来学习划分这两个类别:

线性分类器采用仿射变换(prediction = W•input + b),我们对其进行训练,使预测值与目标值之差的平方最小化。

接下来,咱们来创建变量W和b,分别用随机值和零进行初始化:

# 输入是二维点
input_dim = 2

# 每个样本的输出预测值是一个分数值(如果分类器预测样本属于类别0,那么这个分数值会接近0;如果预测样本属于类别1,那么这个分数值会接近1)
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,)))

下面是前向传播函数。

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

因为这个线性分类器处理的是二维输入,所以W实际上只包含两个标量系数W1和W2

W = [[w1], [w2]],b则是一个标量系数

因此,对于给定的输入点[x,y],其预测值为

prediction = [[w1], [w2]]•[x, y] + b = w1 * x + w2 * y + b。

下面是均方误差损失函数:

def square_loss(targets, predictions):

    # per_sample_losses是一个与targets和predictions具有相同形状的张量,其中包含每个样本的损失值
    per_sample_losses = tf.square(targets - predictions)

    # 我们需要将每个样本的损失值平均为一个标量损失值,这由reduce_mean来实现
    return tf.reduce_mean(per_sample_losses)

接下来就是训练步骤,即接收一些训练数据并更新权重W和b,以使数据损失值最小化

下面是训练步骤函数:

learning_rate = 0.1

def training_step(inputs, targets):

    # (本行及以下2行)在一个梯度带作用域内进行一次前向传播
    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])

    # (本行及以下1行)更新权重
    W.assign_sub(grad_loss_wrt_W * learning_rate)
    b.assign_sub(grad_loss_wrt_b * learning_rate)

    return loss

为简单起见,我们将进行批量训练,而不是小批量训练,即在所有数据上进行训练(计算梯度并更新权重),而不是小批量地进行迭代。

一方面,每个训练步骤的运行时间要长得多,因为我们要一次性计算2000个样本的前向传播和梯度。

另一方面,每次梯度更新将更有效地降低训练数据的损失,因为它包含了所有训练样本的信息,而不是只有128个随机样本。

因此,我们需要的迭代次数更少,而且应该使用比通常用于小批量训练更大的学习率(我们将使用 learning_rate =0.1)。如下代码所示:展示了批量训练循环

for step in range(40):
    loss = training_step(inputs, targets)
    print(f"Loss at step {step}: {loss:.4f}")

经过40次迭代之后,训练损失值似乎稳定在0.025左右(0.0266)。

我们来绘制一下这个线性模型如何对训练数据点进行分类,由于目标值是0和1,因此如果一个给定输入点的预测值小于0.5,那么它将被归为类别0,而如果预测值大于0.5,则被归为类别1,如下图所示:

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

模型对训练数据的预测结果:与训练数据的目标值非常接近。

对于给定点[x, y],其预测值是prediction = [[w1],[w2]]•[x, y] + b = w1 * x + w2 * y + b。

因此,类别0的定义是w1 *x + w2 * y + b < 0.5,类别1的定义是w1 * x + w2 * y + b > 0.5。

你会发现,这实际上是二维平面上的一条直线的方程:w1 * x + w2 * y + b = 0.5。

在这条线上方的点属于类别1,下方的点属于类别0,你可能习惯看到像y = a * x + b这种形式的直线方程,如果将我们的直线方程写成这种形式,那么它将变为:y = - w1 / w2 * x + (0.5 - b) / w2。

我们来绘制这条直线:

# 在−1和4之间生成100个等间距的数字,用于绘制直线
x = np.linspace(-1, 4, 100)

# 直线方程
y = - W[0] /  W[1] * x + (0.5 - b) / W[1]

# 绘制直线("-r"的含义是“将图像绘制为红色的线”)3
plt.plot(x, y, "-r")

# 在同一张图上绘制模型预测结果
plt.scatter(inputs[:, 0], inputs[:, 1], c=predictions[:, 0] > 0.5)

        咱们将模型可视化为一条直线。

        这就是线性分类器的全部内容找到一条直线(或高维空间中的一个超平面)的参数,将两类数据整齐地分开。


这就是实现线性分类器的全部内容啦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/387243.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ps:焦点堆栈

焦点堆栈 Focus Stacking是一种摄影和图像处理技术&#xff0c;通过合并多张在不同焦距拍摄的照片来创建一张具有更大景深的图像&#xff0c;特别适用于微距摄影、风景摄影和任何需要在整个场景中保持尖锐对焦的情况。 ◆ ◆ ◆ 拍摄注意事项 1、使用三脚架 为了确保图像之间…

Spring 事务原理总结四

作为一名认知有限的中国人&#xff0c;我对年的喜爱&#xff0c;胜过其他一切&#xff0c;因为它给了我拒绝一切的合理理由。每到这个时候&#xff0c;我都会用各种理由来为自己的不作为开脱&#xff0c;今年亦是如此。看着频频发出警报的假期余额&#xff0c;我内心的焦躁变得…

VSCode无法连接远程服务器的两种解决方法

文章目录 VSCode Terminal 报错解决方式1解决方式2you are connected to an OS version that is unsupported by Visual Studio Code解决方法 VSCode Terminal 报错 直接在terminal或cmd中使用ssh命令可以连接服务器&#xff0c;但是在vscode中存在报错&#xff0c;最后一行为…

第12讲创建图文投票实现

创建图文投票实现 图文投票和文字投票基本一样&#xff0c;就是在投票选项里面&#xff0c;多了一个选项图片&#xff1b; <view class"option_item" v-for"(item,index) in options" :key"item.id"><view class"option_input&qu…

第13章 网络 Page724 asio定时器

程序代码&#xff1a; 11行&#xff0c;声明一个ios对象 13行&#xff0c;使用ios对象作为参数声明一个定时器&#xff0c;此时&#xff0c;定时器和ios完成了关联&#xff0c;后面定时器如果有任务的话&#xff0c;就可以将任务交给ios 16行&#xff0c;为定时器设置一个定…

Vue3+Vite+TS+Pinia+ElementPlus+Router+Axios创建项目

目录 初始项目组成1. 创建项目1.1 下载项目依赖1.2 项目自动启动1.3 src 别名设置vite.config.ts配置文件tsconfig.json配置若新创项目ts提示1.4 运行测试2. 清除默认样式2.1 样式清除代码下载2.2 src下创建公共样式文件夹`style`2.3 main.js中引入样式2.4 安装`sass`解析插件2…

Unity(单元测试)在STM32上的移植与应用

概述 Unity Test是一个为C构建的单元测试框架。本文基于STM32F407为基础&#xff0c;完全使用STM32CubeIDE进行开发&#xff0c;移植和简单使用Unity。 单片机型号&#xff1a;STM32F407VET6 软件&#xff1a;STM32CubeIDE Version: 1.14.1 Unity Version&#xff1a;2.…

小结与数字的魅力的开篇

小结 本系列主要介绍了一些排序算法&#xff0c;包括冒泡排序、快速排序、直接插入排序、希尔排序、简单选择排序、堆排序、归并排序、计数排序、桶排序和基数排序。 排序算法本身并不难&#xff0c;但其涉及的知识点却星罗棋布&#xff0c;其变化莫测的思路更让人难以捉摸&am…

【数据结构】哈希桶封装出map和set

利用之前的哈希桶封装出unordered_map和unordered_set。 这个封装并不简单&#xff0c;迭代器的使用&#xff0c;模板参数的繁多&#xff0c;需要我们一层一层封装。 map是一个k - v类型&#xff0c;set是k类型&#xff0c;那么就明确了如果需要封装&#xff0c;底层的tables…

Python算法探索:从经典到现代(三)

一、引言 随着信息技术的飞速发展&#xff0c;数据已经成为现代社会不可或缺的资源。Python&#xff0c;作为数据处理和分析的利器&#xff0c;为我们提供了大量强大的库和工具&#xff0c;用于从经典到现代的各种算法探索。本文将带你领略Python在算法领域的魅力&#xff0c;从…

OpenAI宣布ChatGPT新增记忆功能;谷歌AI助理Gemini应用登陆多地区

&#x1f989; AI新闻 &#x1f680; OpenAI宣布ChatGPT新增记忆功能&#xff0c;可以自由控制内存&#xff0c;提供个性化聊天和长期追踪服务 摘要&#xff1a;ChatGPT新增的记忆功能可以帮助AI模型记住用户的提问内容&#xff0c;并且可以自由控制其内存。这意味着用户不必…

mysql5.6安装---windows版本

安装包下载 链接&#xff1a;https://pan.baidu.com/s/1L4ONMw-40HhAeWrE6kluXQ 提取码&#xff1a;977q 安装视频 1.解压完成之后将其放到你喜欢的地址当中去&#xff0c;这里我默认放在了D盘&#xff0c;这是我的根目录 2.配置环境变量 我的电脑->属性->高级->环境…

今日arXiv最热NLP大模型论文:清华提出LongAlign,打破长上下文对齐瓶颈,数据、训练策略、评估基准一网打尽

随着LLMs的发展&#xff0c;其支持的上下文长度越来越长。仅一年时间&#xff0c;GPT-4就从一开始的4K、8K拓展到了128k。 128k什么概念&#xff1f;相当于一本300页厚的书。这是当初只支持512个tokens的BERT时代不敢想象的事情。 随着上下文窗口长度的增加&#xff0c;可以提…

【优化数学模型】1. 基于Python的线性规划问题求解

【优化数学模型】1. 基于Python的线性规划问题求解 一、线性规划问题1.概述2.三要素 二、示例&#xff1a;药厂生产问题三、使用 Python 绘图求解线性规划问题1.绘制约束条件2.绘制可行域3.绘制目标函数4.绘制最优解 四、使用 scipy.optimize 软件包求解线性规划问题1.导入库2.…

springboot742餐厅点餐系统

springboot742餐厅点餐系统 获取源码——》公主号&#xff1a;计算机专业毕设大全

面试前的准备

面试前的准备 Java程序员校招与社招的区别 校招和社招都是企业招聘形式的一种&#xff0c;只是面向的对象不同。校招 只允许在校生参加&#xff0c;社招理论上是任何人都能参加的(包括在校生)。 但是&#xff0c;无论是社招还是校招&#xff0c;它的难度都取决于你的水平高低。…

【Win10 触摸板】在插入鼠标时禁用触摸板,并在没有鼠标时自动启用触摸板。取消勾选连接鼠标时让触摸板保持打开状态,但拔掉鼠标后触摸板依旧不能使用

出现这种问题我的第一反应就是触摸板坏了&#xff0c;但是无意间我换了一个账户发现触摸板可以用&#xff0c;因此推断触摸板没有坏&#xff0c;是之前的账户问题&#xff0c;跟系统也没有关系&#xff0c;不需要重装系统。 解决办法&#xff1a;与鼠标虚拟设备有关 然后又从知…

Redis笔记

Redis&#xff08;Remote Dictionary Server&#xff09;是一种非关系型数据库 Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a; Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加载进行使用。Redis不仅仅…

C# Winform .net6自绘的圆形进度条

using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms;namespace Net6_GeneralUiWinFrm {public class CircularProgressBar : Control{private int progress 0;private int borderWidth 20; // 增加的边框宽度public int Progr…

Qt:槽函数的五种写法

一、Qt4写法&#xff08;不推荐&#xff09; connect(ui.btnOpen,SIGNAL(clicked),this,SLOT( open() ) );因为是以宏定义的方式展开&#xff0c;所以如果SIGNAL写错&#xff0c;或者信号名字、槽函数写错、编译器是无法检验出来的&#xff0c;导致出现隐性BUG&#xff0c;不容…