Rust机器学习之ndarray

news2024/12/24 22:09:27

Rust机器学习之ndarray

众所周知,Python之所以能成为机器学习的首选语言,与其丰富易用的库有很大关系。某种程度上可以说是诸如numpypandasscikit-learnmatplotlibpytorchnetworks…等一系列科学计算和机器学习库成就了Python今天编程语言霸主的地位。基本上今天的机器学习任务主要就建立在上面列举的这6个库上。这6个库在Rust上都有对应的替代方案,从本章开始,我会带大家一起学习如何使用Rust及其库替代Python来更好得完成机器学习任务。

Python库Rust替代方案教程
numpyndarrayRust机器学习之ndarray
pandasPolars Rust机器学习之Polars
scikit-learnLinfa SmartcoreRust机器学习之Linfa
pytorchtch-rsRust机器学习之tch-rs
networkspetgraphRust机器学习之petgraph
matplotlibplottersRust机器学习之plotters

数据和算法工程师偏爱Jupyter,为了跟Python保持一致的工作环境,文章中的示例都运行在Jupyter上。因此需要各位搭建Rust交互式编程环境(让Rust作为Jupyter的内核运行在Jupyter上),相关教程请参考 Rust交互式编程环境搭建

ndarray是个啥?

ndarray是Rust生态中用于处理数组的库。它包含了所有常用的数组操作。简单地说ndarray相当于Rust的numpy

除了数组操作,ndarray还通过派生包提供其他丰富功能,比如

  • ndarray-linalg 用于线性代数运算;
  • ndarray-rand 用于产生随机数;
  • ndarray-stats 用于统计计算;

可以说ndaary不但包含了numpy的功能,还包含了部分scipy的功能。

值得一提的是,ndarray还很好地支持很多外部特性。比如可以支持 rayon做并行计算,支持BLAS进行底层运算加速。因此ndarray的性能非常彪悍。

ndarray 的 BLAS是通过 blas-src 实现的,blas-src为ndarray提供可选的BLAS源,目前支持

  • accelerate - 苹果的Accelerate 框架(仅支持Mac系统)
  • blis - BLIS是一个类BLAS高性能线性代数库
  • intel-mkl - Intel-MKL是⼀套经过高度优化和广泛线程化的数学库
  • netlib - Netlib是一系列数学工具的集合,其中包含BLAS和LAPACK实现
  • openblas - OpenBLAS是一套高度优化的开源BLAS库

为啥要用ndarray?

Rust本身已经自带了数组(或列表)类型,也有vector容器。得益于Rust自身的强大,这些自带的数据类型或容器比很多其他编程语言都要快,甚至快一个数量级。那么我们为什么还需要ndarray呢?

首先,ndarray是专门为处理n维数组(矩阵)而设计的,里面包含了很多数学运算,比如矩阵相乘、矩阵求逆等。

其次,ndarray支持SIMD(Single Instruction Multiple Data),可以进一步提升计算性能。

SIMD 的全称是 Single Instruction Multiple Data,中文名“单指令多数据”。顾名思义,一条指令处理多个数据。

在这里插入图片描述

如上图所示

(a) 是普通的标量计算,加法指令一次只能对两个数执行一个加法操作。

(b)是SIMD向量计算,SIMD加法指令一次可以对两个数组(向量)执行加法操作。

ndarray简明教程

看了上面的介绍我猜你已经迫不及待想尝试一下ndarray的威力了。下面我们开始ndarray的学习。

首先让我们引入ndarray包。

传统的Rust程序有Cargo进行包管理,只需要在cargo.toml[dependencies]中加入

ndarray = "0.15.6"

然后在main.rs开头加入

use ndarray::prelude::*;

即可引入ndarrayprelude涵盖了几乎一切我们需要的模块。

但是在交互环境下没有Cargo,我们需要用evcxr:dep命令来引入包。在Jupyter中输入如下代码:

:dep ndarray = {version = "0.15.6"}

use ndarray::prelude::*;

创建数组

我们先来看一下如何创建数组:

let arr = array![1., 2., 3., 4., 5., 6.];
println!("1D array: {}", arr);

ndarray提供了array!宏来创建数组。array!宏会自动判断创建那种类型的ArrayBase。上面的示例代码创建了一个1-D类型的ArrayBase,即一维数组。基类ArrayBase实现了std::fmt::Display方法,因此创建的对象可以直接利用println!宏输出。

我们比较一下ndarray与rust自带数组和vec的区别:

let arr1 = array![1., 2., 3., 4., 5., 6.];
println!("1D array: \t{}", arr1);

let ls1 = [1., 2., 3., 4., 5., 6.];
println!("1D list: \t{:?}", ls1);

let vec1 = vec![1., 2., 3., 4., 5., 6.];
println!("1D vector: \t{:?}", vec1);

输出结果如下:

1D array: 	[1, 2, 3, 4, 5, 6]
1D list: 	[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
1D vector: 	[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]

注意,array!将浮点型转成了整形,因为给定的数小数位都为0。

向量加法

下面我们让两个数组中的元素两两相加:

let arr2 = array![1., 2.2, 3.3, 4., 5., 6.];
let arr3 = arr1 + arr2;
println!("1D array: \t{}", arr3);

对比Rust自带数组和vec的实现,你就能发现ndarray多么简单自然。

let arr2 = array![1., 2.2, 3.3, 4., 5., 6.];
let arr3 = arr1 + arr2;
println!("1D array: \t{}", arr3);

let ls2 = [1., 2.2, 3.3, 4., 5., 6.];
let mut ls3 = ls1.clone();
for i in 1..ls2.len(){
    ls3[i] = ls1[i] + ls2[i];
}
println!("1D list: \t{:?}", ls3);

let vec2 = vec![1., 2.2, 3.3, 4., 5., 6.];
let vec3: Vec<f64> = vec1.iter().zip(vec2.iter()).map(|(&e1, &e2)| e1 + e2).collect();
println!("1D vec: \t{:?}", vec3);

输出结果:

1D array: 	[2, 4.2, 6.3, 8, 10, 12]
1D list: 	[1.0, 4.2, 6.3, 8.0, 10.0, 12.0]
1D vec: 	[2.0, 4.2, 6.3, 8.0, 10.0, 12.0]

上面的对比可以发现,Rust自带数组和vec都需要循环或迭代逐个元素相加求值,而ndarray只需要简单的+即可,即简单又直观。对数据和算法工程师来说,ndarray可以极大提高开发效率。

二维数组和高维数组

接下来的演示就不再给大家比较Rust自带数组和vec的实现了。上一节已经充分说明用Rust自带数据类型和数据结构来实现数组操作非常繁琐,而ndarray则十分简洁直观。所以下面我们将聚集ndarray

ndarray提供多种方法创建和实例化二维数组。看下面代码:

let arr4 = array![[1., 2., 3.], [ 4., 5., 6.]];
let arr5 = Array::from_elem((2, 1), 1.);
let arr6 = arr4 + arr5;
println!("2D array:\n{}", arr6);

输出如下:

2D array:
[[2, 3, 4],
 [5, 6, 7]]

上面代码展示了2种创建二维数组的方法。

  • array!宏需要指定数组中的所有元素
  • Array::from_elem需要我们给定Shape(在上面案例中是(2,1))和填充元素(在上面案例中是1.0),该方法会用给定元素填充指定大小的二维数组

ndarray还提供了快速创建常用二维数组(矩阵)的方法。看下面代码:

let arr7 =  Array::<f64, _>::zeros(arr6.raw_dim());
let arr8 = arr6 * arr7;
println!("{}", arr8);

输出:

[[0, 0, 0],
 [0, 0, 0]]

Array::zeros(Shape)用0填充指定Shape大小的矩阵。

这里的<f64, _>要解释一下,因为Rust有着严格的类型,编译器有时无法通过0推断出数据类型,所以需要我们显式地告诉编译器这里填充的数据是什么类型。后面的_表示让编译器推断这里的数据类型,此处的数据类型是Shape

.raw_dim()方法我想你已经猜到了它的作用,没错,就是返回矩阵的维度(dimension)。

在线性代数和机器学习中经常会用到单位矩阵(主对角线元素为1,其余元素为0的二维矩阵)。ndarray提供了专门的eye方法来生成单位矩阵。请看下面的代码:

let identity: Array2<f64> = Array::eye(3);
println!("\n{}", identity);

输出:

[[1, 0, 0],
 [0, 1, 0],
 [0, 0, 1]]

eye方法需要我们传入矩阵大小,因为单位矩阵一定是方阵,所以只需要传一个整数即可。这里要注意的是identity的类型我们明确定义为Array2类型,Array2ArrayBase子类,表示一个二位数组。

单位矩阵(数学上一般用 I I I表示)有很多性质,比如任何矩阵乘以单位矩阵都等于它本身: A ⋅ I = A = I ⋅ A A · I = A = I · A AI=A=IA,就像我们经典代数中的1一样。我们可以验证一下单位矩阵的这个性质。

let arr9 = array![[1., 2., 3.], [ 4., 5., 6.], [7., 8., 9.]];
let arr10 = arr9 * identity;
println!("{}", arr10);

结果输出:

[[1, 0, 0],
 [0, 5, 0],
 [0, 0, 9]]

输出结果似乎不是arr9本身。这是因为矩阵运算中*·不是一回事。*也叫标量乘法,是将两个矩阵对应位置元素相乘。而·是将前一个矩阵的行向量与后一个矩阵的列向量相乘。因此·对矩阵的维度有要求,且不满足交换律。相比*,·乘在机器学习中用的更多,ndarray提供了dot()函数来计算·乘。

let arr11: Array2<f64> = arr9.dot(&identity);
println!("{}", arr11);

这次输出就跟我们的预期一致了:

[[1, 2, 3],
 [4, 5, 6],
 [7, 8, 9]]

同样的道理,我们可以声明3维度或更高维度的数组。例如:

let arr12 = Array::<i8, _>::ones((2, 3, 2, 2));
println!("MULTIDIMENSIONAL\n{}", arr12);

输出的是一个 2 × 3 × 2 × 2 2 \times 3 \times 2 \times 2 2×3×2×2数组

MULTIDIMENSIONAL
[[[[1, 1],
   [1, 1]],

  [[1, 1],
   [1, 1]],

  [[1, 1],
   [1, 1]]],


 [[[1, 1],
   [1, 1]],

  [[1, 1],
   [1, 1]],

  [[1, 1],
   [1, 1]]]]

用ndarray-rand生成随机数

ndarray-rand提供了比rand更强大的随机功能,且能更好得跟ndarray配合。

要想在交互编程环境中引入ndarray-rand,需要加入如下代码:

:dep ndarray-rand={version = "0.14.0"}

use ndarray_rand::{RandomExt, SamplingStrategy};
use ndarray_rand::rand_distr::Uniform;

下面我们生成一个大小为 2 × 5 2 \times 5 2×5的在满足[1, 10]之间正态分布的矩阵。

let arr13 = Array::random((2, 5), Uniform::new(0., 10.));
println!("{:5.2}", arr13);

输出结果为:

[[ 1.64,  8.13,  5.92,  5.88,  0.45],
 [ 7.13,  5.79,  8.21,  4.64,  6.04]]

上面的代码每次运行输出结果都不同,因为每次都是随机生成的,但生成的随机数一定满足[1, 10]之间的正态分布。

我们还可以实现类似Python中random.choice()的功能,随机从数组中选取元素

let arr14 = array![1., 2., 3., 4., 5., 6.];
let arr15 = arr14.sample_axis(Axis(0), 2, SamplingStrategy::WithoutReplacement);
println!("Sampling from:\t{}\nTwo elements:\t{}", arr14, arr15);

输出结果为:

Sampling from:	[1, 2, 3, 4, 5, 6]
Two elements:	[1, 3]

接下来给大家演示另一种随机采样方法,这种方法需要用到rand包。

首先我们引入需要用到的模块

use ndarray_rand::rand as rand;
use rand::seq::IteratorRandom;

这里我们将ndarray-rand包中的rand重命名为rand

然后我们就可以仿照rand文档中的示例,用ndarray-rand实现相同的功能

let mut rng = rand::thread_rng();
let faces = "😀😎😐😕😠😢";
let arr16 = Array::from_shape_vec((2, 2), faces.chars().choose_multiple(&mut rng, 4)).unwrap();
println!("Sampling from:\t{}", faces);
println!("Elements:\n{}", arr16);

上面的代码,我们首先创建了一个thread_rng实例,然后创建一个字符串保存一组emoji字符,接下来的代码将从这个字符串中随机选取4个字符,组成一个大小为 2 × 2 2 \times 2 2×2的矩阵。这里随机选取用到了IteratorRandom中的choose_multiple方法,随机从字符串中选择4个字符。

这里注意,choose_multiple的采样数量不能超过字符串中的字符数,否则会报panic。

另外,Array::from_shape_vec返回的是Result类型,表示能否创建给定大小的数组。我们上面的代码简单的unwrap()一下获取Result中的结果。

最终输出结果为:

Sampling from:	😀😎😐😕😠😢
Elements:
[[😠, 😢],
 [😐, 😕]]

用ndarray-stats实现统计

接下来我们看一下ndarray-stats,同时介绍一个经常与ndarray-stats配合使用的包noisy_float

我们将利用ndarray-stats 来验证一下 ndarray-rand生成的正态分布是否真的是正态分布。

首先,我们生成一个大小为 10000 × 2 10000 \times 2 10000×2的矩阵,矩阵中的元素值满足标准正态分布。

use ndarray_rand::rand_distr::{Uniform, StandardNormal}; 

let arr17 = Array::<f64, _>::random_using((10000,2), StandardNormal, &mut rand::thread_rng());

接下来,我们引入需要用到的统计模块。首先将ndarray-statsnoisy_float引入交互式编程环境:

:dep ndarray-stats = {version="0.5.1"}
:dep noisy_float = {version="0.2.0"}

然后引入如下模块:

use ndarray_stats::HistogramExt;
use ndarray_stats::histogram::{strategies::Sqrt, GridBuilder};
use noisy_float::types::{N64, n64};

接着我们需要将数组中的元素从float转为noisy float。这里我不过多的解释noisy float,你可以简单的将noisy float理解为就是一个float,但是当赋与其非法值时(如NaN)就会报panic。由于noisy float不存在非法值,所以它是安全可排序的,这是ndarray-stats创建直方图等操作必要的条件。noisy float的具体介绍请参考noisy_float文档。

要对ndarray中每一个元素进行操作,我们可以用mapv()mapv()map()类似,传入一个闭包对每一个迭代值机型操作。

let data = arr17.mapv(|e| n64(e));

将数组中的元素转成noisy float后,我们就可以创建统计直方图了。直方图需要以一定的步长切分数据,在ndarray-stats中称之为gridndarray-stats提供了GridBuilder来完成数据切分工作。这里我们使用strategies::Sqrt(很多应用都采用此策略,例如我们熟悉的Excel)来帮我们自动推断最佳的切分步长。

let grid = GridBuilder::<Sqrt<N64>>::from_array(&data).unwrap().build();

有了切分步长后我们就可以将其应用到原始数据中得到直方图数据

let histogram = data.histogram(grid);

我们关注的是每个区间内元素的数量,所以我们可以用counts()方法来统计数量

let histogram_matrix = histogram.counts();

这里得到的histogram_matrix 是一个计数矩阵,统计了每个grid内元素的数量。我们的直方图一般是按列统计的,所以我们需要将histogram_matrix按有轴累加起来,这样得到的一维数组就是直方图的统计计数了。

let data = histogram_matrix.sum_axis(Axis(0));
println!("{}", data);

输出结果为:

[2, 0, 1, 0, 5, 5, 1, 4, 9, 2, 4, 12, 8, 13, 17, 11, 20, 32, 32, 30, 32, 43, 47, 66, 65, 89, 110, 114, 110, 131, 147, 170, 173, 194, 209, 206, 255, 259, 264, 280, 292, 255, 288, 294, 335, 300, 306, 324, 287, 323, 275, 280, 284, 250, 244, 247, 192, 230, 178, 191, 162, 147, 150, 128, 110, 99, 83, 93, 83, 68, 47, 50, 30, 32, 34, 21, 24, 13, 14, 15, 9, 12, 4, 4, 7, 2, 3, 1, 0, 1, 1, 1, 0, 0, 3, 0, 0, 0, 0, 2, 0]

虽然输出结果不如图像直观,但也能感觉到确实是服从正态分布的。为了让大家直观看到结果,我们来将数据可视化。数据可视化是数据分析和机器学习中非常重要的一块,后面我会专门一张讲解如何在交互环境下用plotters进行数据可视化。在这里主要让大家可视化地看一下结果。

:dep plotters = {version="0.3.4", default_features = false, features = ["evcxr", "all_series", "all_elements"]}

extern crate plotters;
use plotters::prelude::*;
let figure = evcxr_figure((640, 480), |root| {
    root.fill(&WHITE);
    let histogram_matrix = histogram.counts();
    let data = histogram_matrix.sum_axis(Axis(0));
    let mut chart = ChartBuilder::on(&root)
        .set_label_area_size(LabelAreaPosition::Left, 40)
        .set_label_area_size(LabelAreaPosition::Bottom, 40)
        .caption("随机数分布图", ("sans-serif", 30))
        .build_cartesian_2d((0..data.len()).into_segmented(), 0..300)
        .unwrap();

    chart
        .configure_mesh()
        .disable_x_mesh()
        .bold_line_style(&WHITE.mix(0.3))
        .axis_desc_style(("sans-serif", 15))
        .draw()?;

    chart.draw_series((0..).zip(data.iter()).map(|(x, y)| {
        let x0 = SegmentValue::Exact(x);
        let x1 = SegmentValue::Exact(x + 1);
        let mut bar = Rectangle::new([(x0, 0), (x1, (*y) as i32)], RED.filled());
        bar.set_margin(0, 0, 1, 1);
        bar
    }))?;

    Ok(())
});
figure

上面代码会将data中的数据以柱状图的形式显示。

在这里插入图片描述

结论

本章我们学习了如何使用ndarray以及它与自带数组和vec的区别。我们还学习了ndarray生态中随机和统计相关的包,这些包一定程度上提供了scipy的相关功能。

其实ndarray的功能远远不止这些,基本上numpy有的功能ndarray都有对应的实现。这篇文章详细对比了ndarraynumpy的函数功能,熟悉numpy的同学可以对比着学ndarray,效率会非常高。后面我也会专门做一篇numpyndarray方法的详细对比。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue项目实战——【基于 Vue3.x + NodeJS】实现的课程表排课系统二(week-title)

文章目录基于 Vue3.x NodeJS实现的课程表排课系统&#xff08;二&#xff09;初始化样式封装axios处理数据表格头部&#xff08;周几&#xff09;子组件写入根组件App.vue浅拿一下数据基于 Vue3.x NodeJS实现的课程表排课系统&#xff08;二&#xff09; 初始化样式 src/ass…

当 AI 遇上 web3,会碰撞出什么火花?

2020 年之前&#xff0c;Web3 的路是创造者们铺好的。但 Web3 遇上了金融&#xff0c;这出乎了每个创造者的意料之外&#xff0c;稳定币、AMM 和借贷突其来地点燃了2020年的那个夏天。之后 Web3 又遇到了 NFT、游戏和元宇宙。不过因为技术限制&#xff0c;除了金融之外&#xf…

AVL树详解(附带旋转步骤图,手把手带你上分)

文章目录&#x1f44d; AVL树的概念先了解一下&#x1f601;AVL树节点的定义&#x1f60a;AVL树插入节点&#x1f91e;AVL树为什么要旋转&#x1f60d;AVL树的四种旋转左单旋右单旋左右双旋右左双旋❤️结语关于AVL树的讲解 &#x1f44d; AVL树的概念先了解一下 它的左右子树…

Scala005--Scala中的数据结构【集合】之数组

Scala中的数据结构和Java中一样&#xff0c;都有数组&#xff0c;列表&#xff0c;集合&#xff0c;映射。在Scala中与Java不同的是数组可以有可变数组&#xff0c;而不是一旦定义就不可以进行更改。我们来认识数组&#xff0c;并使用相应的代码去查看具体的实现效果。 目录 …

Linux命令从入门到实战 ----进程管理

文章目录kill终止进程pstree查看进程树netstat网络状态和端口占用信息crontab定时任务进程(process): 是正在执行的一个程序或命令&#xff0c;每一个进程都是一个运行的实体&#xff0c;都有自己的地址空间&#xff0c;并占用一定的系统资源 服务(service)&#xff1a;启动之后…

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ

深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ 文章目录深度学习 Day 18——利用卷神经网络实现猫狗识别 Ⅱ一、前言二、我的环境三、前期工作1、导入依赖项并设置GPU2、导入数据四、数据预处理1、加载数据2、检查数据3、配置数据集并进行归一化处理4、可视化数据五、构建…

xv6源码解析(三)——内存管理

01 内存管理 内存管理&#xff1a;通过编写物理页分配器&#xff0c;以链表管理所有空闲页帧&#xff0c; 实现了对物理页帧的回收与分配&#xff1b;在xv6系统sbrk内存管理方式的基础上&#xff0c;添加了进程用户空间非连续分区的分配。 内存管理参考链接 mmap 02 sbrk机制…

猿创征文|【Linux】Linux中的gdb调试器的使用

目录 一、什么是gdb&#xff1f; 二、gdb的安装 三、gdb的使用 1、只有debug版可以被调试 2、gdb的常用指令 2.1显示代码&#xff1a;l指令&#xff08;list指令&#xff09; 2.2设置断点&#xff1a;b指令&#xff08;break指令&#xff09; 2.3显示断点/禁用断点/开启…

Linux操作系统~进程fork到wait到底怎么用?

目录 1.fork() &#xff08;1&#xff09;.概念 &#xff08;2&#xff09;.fork的写时拷贝 &#xff08;3&#xff09;.fork常规用法 2.进程终止 &#xff08;1&#xff09;.进程退出场景/退出码 &#xff08;2&#xff09;.进程常见退出方法 1&#xff09;.exit函数 …

类与对象(中级)

目录 1. 包 1.1 包的概念 1.2 导入包中的类 1.3 自定义包 1.4 常见的包 2. Java三大特性 -- 封装 3. Java三大特性 -- 继承 3.1 继承的概念&#xff1a; 3.2 继承的语法 3.3 父类成员访问 3.3.1 子类中访问父类的成员变量 3.3.2 子类中访问父类的成员方法 4. supe…

数据挖掘(六) 层次聚类

数据挖掘&#xff08;六&#xff09; 层次聚类 1.层次聚类简介 层次聚类算法(Hierarchical Clustering)将数据集划分为一层一层的clusters&#xff0c;后面一层生成的clusters基于前面一层的结果。层次聚类算法一般分为两类&#xff1a; Divisive 层次聚类&#xff1a;又称自…

【nacos】5.1 spring cloud + Nacos 实现统一配置管理

1. 解决的问题&#xff1a; 配置动态更新配置集中式管理配置内容的安全性和权限不同部署环境下的配置 2. 环境&#xff1a; ideaspring cloudspring-cloud-alibaba nacosmavenmqtt &#xff08;客户端&#xff0c;服务器采用的是EMQ X 5.0 &#xff09; 3. pom依赖 3.1 父级…

皮卡丘python turtle海龟绘图(电力球版)附源代码

皮卡丘python turtle海龟绘图&#xff08;电力球版&#xff09;附源代码 本文目录&#xff1a; 一、皮卡丘python turtle海龟成品效果图 二、皮卡丘背景介绍 三、皮卡丘卡角色形象 四、愿你拥有一只皮卡丘 五、Python 海龟画图主要方法 &#xff08;1&#xff09;海龟画图…

Allegro在PCB上制作二维码和条形码操作指导

Allegro在PCB上制作二维码和条形码操作指导 当我们需要在PCB放置一个二维码或者条形码的时候,可以不需要额外去贴标签,可以直接在PCB上制作,如下图 制作出来的二维码和条形码是可以直接用扫码程序扫描的 具体操作步骤如下 首先要用was performance allegro productivity…

python Clickhouse 分布式表介绍和创建、插入和查询数据,以及解决遇到的问题

目录 一、分布式表和本地表 原理解析&#xff1a; 二、Clickhouse创建分布式表结构 三、python代码实现&#xff08;亲测有效&#xff09; 四、解决遇到的问题 解决 DB::Exception: Missing columns: wefgrgrfew while processing query: wefgrgrfew, required columns: …

【深度学习】第三章:卷积神经网络

文章目录1. 为什么要使用卷积神经网络&#xff1f;2. 卷积2.1 数学上的卷积2.2 深度学习的卷积3. 卷积的构成4. 卷积的特征5. 卷积的计算(1) 一维卷积计算(2) 二维卷积计算(黑白图片)(2) 三维卷积计算(彩色图片)6. 卷积的优势7. 卷积神经网络7.1 卷积层7.2 池化层7.3 全连接层8…

浅聊一下Nginx

目录 Nginx的下载与安装 去Nginx官网安装&#xff1a;nginx news 直接进入下载页面进行安装 直接安装&#xff1a; 在服务器上使用命令对nginx的安装过程 Nginx命令 Nginx配置文件结构 Nginx配置文件&#xff08;conf/nginx.conf&#xff09;正题分为三个部分&#xff1…

Vue框架的学习(Vue操作指令学习三 V-bind )第三课

Vue框架的学习(Vue操作指令学习三 V-bind )第三课 语法的学习关键在于实操 案例一 V-bind基本操作 通过这个案例了解基本的操作 <div id"app"><img src"./img/1-1 (1).jpg" alt""><!--! 绑定图片利用V-bind指令 --><img v-…

一本通1073;救援(c++)

#include <iostream> #include <cmath> using namespace std; int main() {// 屋顶数目、人数int n, m;// x坐标、y坐标、实际距离、所需时间double x, y, s, sum 0;cin >> n; // 输入屋顶数目for (int i 1; i < n; i){// 输入x、y坐标和人数cin >&g…

Rockland丨艾美捷Rockland大鼠γ-球蛋白说明书

艾美捷Rockland大鼠γ-球蛋白&#xff1a; 大鼠γ-球蛋白组分由含有全抗体和其他非白蛋白血浆蛋白的血清组分组成。丙种球蛋白可用于治疗&#xff0c;以暂时提高患者的免疫力&#xff08;如免疫抑制感染后&#xff09;或增加接受肾移植的可能性。γ-球蛋白级分可作为对照试剂用…