政安晨:示例演绎机器学习中(深度学习)神经网络的数学基础——快速理解核心概念(二){两篇文章讲清楚}

这一篇与上一篇是兄弟篇,意在通过两篇文章讲清楚深度学习中神经网络的数学基础,第一次看到这篇文章的小伙伴可以从上一篇文章看起包括搭建环境等等都在上一篇),上一篇链接如下

政安晨:示例演绎机器学习中(深度学习)神经网络的数学基础——快速理解核心概念(一){两篇文章讲清楚}icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/136089968



张量运算

如果把人工智能领域比作星辰大海,那么机器学习就是渡海之舟,而神经网络就是这舟中的机器

咱们这篇文章要介绍的张量运算,就是这架机器中的齿轮这是基础的基础,也是核心的核心!

计算机程序最终都可以简化为对二进制输入的一些二进制运算(AND、OR、NOR等),与此类似,深度神经网络学到的所有变换也都可以简化为对数值数据张量的一些张量运算(tensor operation)或张量函数(tensor function),如张量加法、张量乘法等

我以前做过一个例子,请见下面这篇文章:

政安晨的机器学习笔记——基于Anaconda安装TensorFlow并尝试一个神经网络小实例icon-default.png?t=N7T8https://blog.csdn.net/snowdenkeke/article/details/135841281在我以前的这个例子里,最后部分尝试了一个神经网络小实例,里面涉及了通过叠加Dense层来构建模型。

keras.layers.Dense(512, activation="relu")

你可以将这个层理解为一个函数,其输入是一个矩阵,返回的是另一个矩阵,即输入张量的新表示。

就像下面这个函数:

output = relu(dot(input, W) + b)

其中W是一个矩阵,b是一个向量,二者都是该层的属性)。

我们将上式拆开来看:这里有3个张量运算,输入张量和张量W之间的点积运算(dot),由此得到的矩阵与向量b之间的加法运算(+)。

relu运算relu(x)就是max(x, 0),relu代表“修正线性单元”(rectified linear unit)。

虽然咱们讲的是数学和算法,但咱们确是始终记得咱们的目标是演绎,程序的事交给程序,用程序来演绎数学和算法是咱们做程序员的下意识行为,呵呵。

逐元素运算

relu运算和加法都是逐元素(element-wise)运算,即该运算分别应用于张量的每个元素。

也就是说,这些运算非常适合大规模并行实现,如果你想对逐元素运算编写一个简单的Python实现,那么可以使用for循环。

下列代码是对逐元素relu运算的简单实现

def naive_relu(x):
    #x是一个2阶NumPy张量
    assert len(x.shape) == 2

    #避免覆盖输入张量
    x = x.copy()

    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] = max(x[i, j], 0)
    return x

对于加法,可采用同样的实现方法

def naive_add(x, y):

    #x和y是2阶NumPy张量
    assert len(x.shape) == 2
    assert x.shape == y.shape

    #避免覆盖输入张量
    x = x.copy()
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] += y[i, j]

    return x

利用同样的方法,可以实现逐元素的乘法、减法等

在实践中处理NumPy数组时,这些运算都是优化好的NumPy内置函数,这些函数将大量运算交给基础线性代数程序集(Basic Linear Algebra Subprograms,BLAS)实现,BLAS是低层次(low-level)、高度并行、高效的张量操作程序,通常用Fortran或C语言来实现。

在NumPy中可以直接进行下列逐元素运算,速度非常快

import numpy as np

# 逐元素加法
z = x + y

# 逐元素relu
z = np.maximum(z, 0.)

我们来看一下两种方法运行时间的差别

方式一 ——> Numpy优化的:

import time

x = np.random.random((20, 100))
y = np.random.random((20, 100))
t0 = time.time()
for _ in range(1000):
    z = x + y
    z = np.maximum(z, 0.)
print("Took: {0:.2f} s".format(time.time() - t0))

这一种运行方式就是被NumPy优化好的{内置函数}(这还是在我的CPU版本的笔记本电脑上运行的))

方式二 ——>咱们现场手工实现的滴:

def naive_add(x, y):
    #x和y是2阶NumPy张量
    assert len(x.shape) == 2
    assert x.shape == y.shape

    #避免覆盖输入张量
    x = x.copy()
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] += y[i, j]

    return x


def naive_relu(x):
    #x是一个2阶NumPy张量
    assert len(x.shape) == 2

    #避免覆盖输入张量
    x = x.copy()

    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] = max(x[i, j], 0)
    return x


t0 = time.time()
for _ in range(1000):
    z = naive_add(x, y)
    z = naive_relu(z)
print("Took: {0:.2f} s".format(time.time() - t0))

小伙伴们看到了吧,咱们临时实现的函数对同样的运算,花了2.75秒,是刚才第一种方法0.01秒的275倍

另外,如果在GPU上运行TensorFlow或者PyTorch代码,逐元素运算都是通过完全向量化的CUDA来完成的,可以最大限度地利用高度并行的GPU芯片架构。

广播

刚才咱们对naive_add的简单实现仅支持两个形状相同的2阶张量相加,但在我文章前面介绍的Dense层中,我们将一个2阶张量与一个向量相加。如果将两个形状不同的张量相加,会发生什么?在没有歧义且可行的情况下,较小的张量会被广播(broadcast),以匹配较大张量的形状。广播包含以下两步

A . 向较小张量添加轴[叫作广播轴(broadcast axis)],使其ndim与较大张量相同。

B . 将较小张量沿着新轴重复,使其形状与较大张量相同。

举例来说(x的形状是(32, 10),y的形状是(10,)):

import numpy as np

# x是一个形状为(32, 10)的随机矩阵
x = np.random.random((32, 10))

# y是一个形状为(10,)的随机向量
y = np.random.random((10,))

咱们可以像这样查看一下x 与 y的值分别是什么(我是在本地环境的Jupyter Notebook中运行的)?

上面这个张量,是一个32行10列的二阶张量(也就是32×10的二阶矩阵)

上面这个是一个向量(咱们姑且可以称为一阶张量)(其实就是10个元素的数组)。

把它俩进行运算的过程是这样滴:

1. 首先,我们向y添加第1个轴(空的),这样y的形状变为(1, 10):

# 现在y的形状变为(1, 10)
y = np.expand_dims(y, axis=0)

您会看到此时y的值为:

注意:上面这里已经是2个中括号哦!

2. 然后,我们将y沿着这个新轴重复32次,这样得到的张量Y的形状为(32, 10),并且Y[i, :] == y for i in range(0, 32):

# 将y沿着轴0重复32次后得到Y,其形状为(32, 10)
y = np.concatenate([y] * 32, axis=0)

此时,您会看到y被复制了32次:

3. 现在,我们可以将X和Y相加,因为它们的形状相同啦。

当然,在实际的实现过程中并不会创建新的2阶张量,因为那样做非常低效。重复操作完全是虚拟的,它只出现在算法中,而没有出现在内存中。但想象将向量沿着新轴重复10次,是一种很有用的思维模型。

下面是一种简单实现

def naive_add_matrix_and_vector(x, y):

    # x是一个2阶NumPy张量
    assert len(x.shape) == 2

    # y是一个NumPy向量
    assert len(y.shape) == 1  
    assert x.shape[1] == y.shape[0]

    # 避免覆盖输入张量
    x = x.copy()
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            x[i, j] += y[j]

    return x

如果一个张量的形状是(a, b, ..., n, n+1, ..., m),另一个张量的形状是(n, n+1, ..., m),那么通常可以利用广播对这两个张量做逐元素运算。广播会自动应用于从a到n-1的轴。

下面这个例子利用广播对两个形状不同的张量做逐元素maximum运算:

import numpy as np

# x是一个形状为(64, 3, 32, 10)的随机张量
x = np.random.random((64, 3, 32, 10))

# y是一个形状为(32, 10)的随机张量
y = np.random.random((32, 10))

# 输出z的形状为(64, 3, 32, 10),与x相同
z = np.maximum(x, y)

咱们来看上面这段代码,x随机了一个4阶张量

y随机了一个2阶张量:

z为张量的Numpy运算

咱们看一下z的形状:

这个形状与x是一样的。

张量积

张量积(tensor product)或点积(dot product)是最常见且最有用的张量运算之一。

注意,不要将其与逐元素乘积(*运算符)弄混,在NumPy中,使用np.dot函数来实现张量积,因为张量积的数学符号通常是一个点(dot)。

下面为点积运算的代码:

x = np.random.random((32,))
y = np.random.random((32,))
z = np.dot(x, y)

数学符号中的点(•)表示点积运算。

z = x•y

咱们可以看到x和y的值

接下来,咱们可以看到点积的计算:z = np.dot(x, y)

小伙伴们看到了吧,两个向量的点积是一个值(也就是一个标量)

从数学角度来看,点积运算做了什么?

我们再看一下两个向量x和y的点积的计算过程:

def naive_vector_dot(x, y):

    # (本行及以下1行) x和y都是NumPy向量
    assert len(x.shape) == 1
    assert len(y.shape) == 1

    assert x.shape[0] == y.shape[0]

    z = 0.

    for i in range(x.shape[0]):
        z += x[i] * y[i]

    return z

可以看到,两个向量的点积是一个标量,而且只有元素个数相同的向量才能进行点积运算

你还可以对一个矩阵x和一个向量y做点积运算,其返回值是一个向量,其中每个元素是y和x每一行的点积,实现过程如下:

def naive_matrix_vector_dot(x, y):

    # x是一个NumPy矩阵
    assert len(x.shape) == 2
    # y是一个NumPy向量
    assert len(y.shape) == 1

    # x的第1维与y的第0维必须大小相同!
    assert x.shape[1] == y.shape[0]
    z = np.zeros(x.shape[0])

    # 这个运算返回一个零向量,其形状与x.shape[0]相同
    for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            z[i] += x[i, j] * y[j]

    return z

你还可以重复使用前面写过的代码,从中可以看出矩阵−向量点积与向量−向量点积之间的关系:

def naive_matrix_vector_dot(x, y):
    z = np.zeros(x.shape[0])
    for i in range(x.shape[0]):
        z[i] = naive_vector_dot(x[i, :], y)
    return z

注意:

只要两个张量中有一个的ndim大于1,dot运算就不再是对称(symmetric)的,也就是说,dot(x, y)不等于dot(y, x)。

当然,点积可以推广到具有任意轴数的张量,最常见的应用可能是两个矩阵的点积。 

对于矩阵x和y,当且仅当x.shape[1] == y.shape[0]时,你才可以计算它们的点积(dot(x, y)),点积结果是一个形状为(x.shape[0],y.shape[1])的矩阵,其元素是x的行与y的列之间的向量点积,简单实现如下:

def naive_matrix_dot(x, y):

    # (本行及以下1行) x和y都是NumPy矩阵
    assert len(x.shape) == 2
    assert len(y.shape) == 2

    # x的第1维与y的第0维必须大小相同!
    assert x.shape[1] == y.shape[0]

    # 这个运算返回一个特定形状的零矩阵
    z = np.zeros((x.shape[0], y.shape[1]))

    # 遍历x的所有行……
    for i in range(x.shape[0]):

        # ……然后遍历y的所有列
        for j in range(y.shape[1]):
            row_x = x[i, :]
            column_y = y[:, j]
            z[i, j] = naive_vector_dot(row_x, column_y)

    return z

为了便于理解点积的形状匹配,可以将输入张量和输出张量像下图那样排列,利用可视化来帮助理解。在下图中,x、y和z都用矩形表示(元素按矩形排列)。

由于x的行和y的列必须具有相同的元素个数,因此x的宽度一定等于y的高度。如果你打算开发新的机器学习算法,可能经常要画这种图。

更一般地说,可以对更高阶的张量做点积运算,只要其形状匹配遵循与前面2阶张量相同的原则。

(a, b, c, d)•(d,)→(a, b, c)

(a, b, c, d)•(d, e)→(a, b, c, e)

张量变形

另一个需要了解的张量运算是张量变形(tensor reshaping)。

虽然我刚讲的神经网络例子的Dense层中没有用到它,但将数据输入神经网络之前,可能在预处理数据时将到了这种运算。

train_images = train_images.reshape((60000, 28 * 28))

张量变形是指重新排列张量的行和列,以得到想要的形状,变形后,张量的元素个数与初始张量相同。

下面这个简单的例子可以帮助我们理解张量变形:

咱们定义一个2阶张量:

x = np.array([[0., 1.], 
              [2., 3.], 
              [4., 5.]]) 

咱们看一下x的形状

咱们将x进行张量变形

x = x.reshape((6, 1)) 

咱们将x再变形:

 x = x.reshape((2, 3)) 

常见的一种特殊的张量变形是转置(transpose),矩阵转置是指将矩阵的行和列互换,即把x[i, :]变为x[:, i]。

>>>   ←----
>>> 
>>> x.shape
(20, 300)

咱们将创建一个形状为(300, 20)的零矩阵:

x = np.zeros((300, 20))

已经创建的矩阵如下:

这个矩阵的形状,现在是:(300,20)

咱们现在对x进行矩阵的转置操作:

x = np.transpose(x)

转置后的形状为(20, 300),如下:

张量运算的几何解释

对于张量运算所操作的张量,其元素可看作某个几何空间中的点的坐标,因此所有的张量运算都有几何解释。以加法为例,假设有这样一个向量:

A = [0.5, 1]

它是二维空间中的一个点(见下图):

咱们在张量运算中的常见做法是将向量描绘成由原点指向这个点的箭头。

假设有另外一个点:B = [1, 0.25],我们将它与前面的A相加。

从几何角度来看,这相当于将两个向量的箭头连在一起,得到的位置表示两个向量之和对应的向量(见下图)。

如你所见,将向量B与向量A相加,相当于将A点复制到一个新位置,这个新位置相对于A点初始位置的距离和方向由向量B决定。如果将相同的向量加法应用于平面上的一组点(一个物体),就会在新位置上创建整个物体的副本(见下图)。

因此,张量加法表示将物体沿着某个方向平移一段距离(移动物体,但不使其变形)。

一般来说,平移、旋转、缩放、倾斜等基本的几何操作都可以表示为张量运算。

机器学习的目的

为高维空间中复杂、高度折叠的数据流形(manifold)找到简洁的表示。

深度学习特别擅长这一点:

它可以将复杂的几何变换逐步分解为一系列基本变换,这与我们展开纸团所采取的策略大致相同。深度神经网络的每一层都通过变换使数据解开一点点,而许多层堆叠在一起,可以实现极其复杂的解开过程。


(更加复杂的知识,咱们将在今后通过实际的示例演绎再为大家讲解。)

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

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

相关文章

Aethir和Well-Link Tech携手革新云游戏,释放人工智能(AI)潜力

​Aethir将为Well-Link Tech的2亿用户提供先进的GPU计算能力,大幅提升他们的游戏体验。 新加坡,2024年2月7日 - 先驱性的去中心化GPU网络Aethir与实时云渲染技术领导者Well-Link Tech携手共创云游戏和元宇宙发展的新时代。 借助Well-Link Tech对领先游戏…

ECMAScript Modules规范的示例详解

ECMAScript Modules(ESM)是JavaScript中用于模块化开发的规范,它允许开发者将代码分割成多个独立的文件,以提高代码的可维护性和可重用性。下面是一个ECMAScript Modules规范的示例详解: 创建模块 1.1 导出变量 在一个…

STL之list容器的介绍与模拟实现+适配器

STL之list容器的介绍与模拟实现适配器 1. list的介绍2. list容器的使用2.1 list的定义2.2 list iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers2.6 list的迭代器失效 3. list的模拟实现3.1 架构搭建3.2 迭代器3.2.1 正向迭代器3.2.2反向迭代器适配…

深度学习技巧应用36-深度学习模型训练中的超参数调优指南大全,总结相关问题与答案

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用36-深度学习模型训练中的超参数调优指南大全,总结相关问题与答案。深度学习模型训练中的调优指南大全概括了数据预处理、模型架构设计、超参数优化、正则化策略和训练技巧等多个关键方面,以提升模型性能和泛化能力。 …

申请SSL证书怎么进行域名验证?域名验证的三种方式

SSL证书是用于加密和保护Web服务器和浏览器之间通信的数字证书,在申请SSL证书时,为了防止域名被冒用,对于申请SSL证书的域名,要求先验证这个域名的所有权。而目前可用的域名验证SSL证书方式有三种:分别是DNS验证、邮箱…

展示wandb的数据

import wandb import matplotlib.pyplot as plt# 初始化 wandb API api wandb.Api()# 假设您想要访问的项目名为 my_project,并且您的 wandb 用户名为 my_username project_name "aicolab/RWKV-5-Test"# 获取项目中的runs runs api.runs(project_name)…

【MySQL】-12 MySQL索引(上篇MySQL索引类型前置-1)

MySQL索引 索引1 索引基础2 索引与优化1 选择索引的数据类型1.1 选择标识符 2 索引入门2.1 索引的类型2.1.1 B-Tree索引2.1.2 Hash索引2.1.3 空间(R-Tree)索引2.1.4 全文(Full-text)索引 索引的优点:索引是最好的解决方案吗? 索引 索引(在MYS…

【51单片机】LCD1602(可视化液晶屏)调试工具的使用

前言 大家好吖,欢迎来到 YY 滴 单片机系列 ,热烈欢迎! 本章主要内容面向接触过单片机的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! YY的《C》专栏YY的《C11》专栏YY…

Stable Video Diffusion图片转视频——Stability AI开源视频模型

我们前期介绍过Stable Diffusion,stable diffusion模型是Stability AI开源的一个text-to-image的扩散模型,其模型在速度与质量上面有了质的突破,玩家们可以在自己消费级GPU上面来运行此模型。 文生图大模型已经火了很长一段时间了&#xff0c…

20240210使用剪映识别字幕的时候的GPU占比RX580-RTX4090

20240210使用剪映识别字幕的时候的GPU占比RX580-RTX4090 2024/2/10 17:54 【使用剪映识别不同的封装格式,不同的音视频编码,对GPU的占用率可能会有比较大的不同!】 很容易发现在在WIN10下使用剪映的时候,X99RX550组合。 GPU部分&…

Stable Diffusion 模型下载:RealCartoon-Realistic - V13

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十下载地址模型介绍 该检查点是 RealCartoon3D 检查点的一个分支。这个目标是在背景和人物中产生更“真实”的外观。我试图避免这个模型中更多的动漫、卡通和“完美”外观。这是一个肯

Linux运行级别 | 管理Linux服务

Linux运行级别 级别: 0关机1单用户2多用户但是不运行nfs网路文件系统3默认的运行级别,给一个黑的屏幕,只能敲命令4未使用5默认的运行级别,图形界面6重启切换运行级别: init x管理Linux服务 systemctl命令&#xf…

〖大前端 - ES6篇②〗- let和const

说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司…

TELNET 远程终端协议

远程终端协议 TELNET TELNET 是一个简单的远程终端协议,也是互联网的正式标准。 用户用 TELNET 就可在其所在地通过 TCP 连接注册(即登录)到远地的另一个主机上(使用主机名或 IP 地址)。 TELNET 能将用户的击键传到…

刘谦魔术我用代码还原了,魔术尽头是数学,数学尽头是神学!

刘谦在春晚让两个半张扑克牌合在一起的时候,我就知道其中必然有数学的奥妙。 假设我们初始卡牌为1,2,3,4。对半撕开后我们定义扑克牌为: 1(1) 2(1) 3(1) 4(1) 1(2) 2(2) 3(2) 4(2)按照刘谦的魔术,你需要…

揭秘企业内团队协作的隐形障碍

企业内团队协作是现代企业中不可避免的一部分。然而在团队协作中,总是会存在一些障碍,这也是企业内团队协作面临的一些挑战。这些障碍会对企业的效率、生产力和团队士气产生影响,因此一定要在团队合作中积极地寻找和消除这些障碍。 一、缺乏透…

华为配置交换机KPI信息上报分析器示例组网图形

配置交换机KPI信息上报分析器示例 组网图形 图1 KPI信息上报拓扑图 组网需求操作步骤配置文件 组网需求 如图1所示,某企业网络用一台华为公司iMaster NCE-CampusInsight作为分析器对交换机设备进行智能运维管理。iMaster NCE-CampusInsight与交换机之间已经实现路由…

2024年 复习 HTML5+CSS3+移动web 笔记 之CSS遍 第6天

6.1 定位-相对和绝对和固定 6.2 相对和绝对和固定 6.3 堆叠顺序z-index 6.4 定位总结 6.5 CSS精灵 基本使用 6.6 案例 CSS精灵 京东服务 6.7 字体图标-下载和使用 6.8 字体图标-上传 6.9 垂直对齐方式vertical-align 6.10 过渡属性 6.11 修饰属性-透明度与光标类型 6.12 综合案…

从0开始图形学(光栅化)

前言 说起图形学,很多人就会提到OpenGL,但其实两者并不是同一个东西。引入了OpenGL加重了学习的难度和成本,使得一些原理并不直观。可能你知道向量,矩阵,纹理,重心坐标等概念,但就是不知道这些概…

Kong 负载均衡

负载均衡是一种将API请求流量分发到多个上游服务的方法。负载均衡可以提高整个系统的响应速度,通过防止单个资源过载而减少故障。 在以下示例中,您将使用部署在两台不同服务器或上游目标上的应用程序。Kong网关需要在这两台服务器之间进行负载均衡&…