现代C++中的从头开始深度学习【2/8】:张量编程
一、说明
初学者文本:此文本需要入门级编程背景和对机器学习的基本了解。张量是在深度学习算法中表示数据的主要方式。它们广泛用于在算法执行期间实现输入、输出、参数和内部状态。
在这个故事中,我们将学习如何使用特征张量 API 来开发我们的C++算法。具体来说,我们将讨论:
- 什么是张量
- 如何在C++中定义张量
- 如何计算张量运算
- 张量约简和卷积
在本文的最后,我们将实现 Softmax 作为将张量应用于深度学习算法的说明性示例。
。
二、什么是张量?
张量是类似网格的数据结构,它概括了任意数量的轴的向量和矩阵的概念。在机器学习中,我们通常使用“维度”这个词而不是“轴”。张量不同维度的数量也称为张量秩:

不同秩张量
在实践中,我们使用张量来表示算法中的数据,并用它们执行算术运算。
我们可以用张量执行的更简单的操作是所谓的元素级操作:给定两个具有相同维度的操作数张量,该操作会产生一个具有相同维度的新张量,其中每个系数的值是从操作数中各个元素的二进制评估中获得的:

系数乘法
上面的例子是两个 2 秩张量的系数乘积的图示。此操作对任何两个张量仍然有效,因为它们具有相同的维度。
像矩阵一样,我们可以使用张量执行其他更复杂的操作,例如矩阵类积、卷积、收缩、约简和无数的几何运算。在这个故事中,我们将学习如何使用特征张量 API 来执行其中一些张量操作,重点介绍对深度学习算法实现最重要的操作。
三、如何在C++中声明和使用张量
众所周知,本征是一个广泛用于矩阵计算的线性代数库。除了众所周知的对矩阵的支持之外,Eigen 还有一个(不支持的)张量模块。
虽然 Eigen Tensor API 表示不受支持,但它实际上得到了 Google TensorFlow 框架开发人员的良好支持。
我们可以使用特征轻松定义张量:
#include <iostream>#include <unsupported/Eigen/CXX11/Tensor>int main(int, char **)
{Eigen::Tensor<int, 3> my_tensor(2, 3, 4);my_tensor.setConstant(42);std::cout << "my_tensor:\n\n" << my_tensor << "\n\n";std::cout << "tensor size is " << my_tensor.size() << "\n\n"; return 0;
}

该行
Eigen::Tensor<int, 3> my_tensor(2, 3, 4);
创建一个张量对象并分配存储整数所需的内存。在此示例中,是一个 3 秩张量,其中第一维的大小为 2,第二维的大小为 3,最后一维的大小为 4。我们可以表示如下:2x3x4my_tensormy_tensor

如果需要,我们可以设置张量数据:
my_tensor.setValues({{{1, 2, 3, 4}, {5, 6, 7, 8}}});std::cout << "my_tensor:\n\n" << my_tensor << "\n\n";

或改用随机值。例如,我们可以做:
Eigen::Tensor<float, 2> kernel(3, 3);
kernel.setRandom();
std::cout << "kernel:\n\n" << kernel << "\n\n";

并在以后使用此内核来执行卷积。我们将很快在这个故事中介绍卷积。首先,让我们学习如何使用TensorMaps。
四、使用 Eigen::TensorMap 创建张量视图
有时,我们分配了一些数据,只想使用张量来操作它。 类似于 但是,它不是分配新数据,而只是作为参数传递的数据的视图。检查以下示例:Eigen::TensorMapEigen::Tensor
//an vector with size 12
std::vector<float> storage(4*3);// filling vector from 1 to 12
std::iota(storage.begin(), storage.end(), 1.);for (float v: storage) std::cout << v << ',';
std::cout << "\n\n";// setting a tensor view with 4 rows and 3 columns
Eigen::TensorMap<Eigen::Tensor<float, 2>> my_tensor_view(storage.data(), 4, 3);std::cout << "my_tensor_view before update:\n\n" << my_tensor_view << "\n\n";// updating the vector
storage[4] = -1.;std::cout << "my_tensor_view after update:\n\n" << my_tensor_view << "\n\n";// updating the tensor
my_tensor_view(2, 1) = -8;std::cout << "vector after two updates:\n\n";
for (float v: storage) std::cout << v << ',';
std::cout << "\n\n";

在这个例子中,很容易看出(默认情况下)特征张量 API 中的张量是 col-major。col-major和row-major是指网格数据如何存储在线性容器中的方式(查看维基百科上的这篇文章):

源
虽然我们可以使用行大张量,但不建议这样做:
目前仅完全支持默认列主布局,因此目前不建议尝试使用行主布局。
Eigen::TensorMap非常有用,因为我们可以使用它来节省内存,这对于深度学习算法等高要求的应用程序至关重要。
五、执行一元和二进制操作
特征张量 API 定义了常见的算术重载运算符,这使得对张量进行编程非常直观和直接。例如,我们可以加减张量:
Eigen::Tensor<float, 2> A(2, 3), B(2, 3);
A.setRandom();
B.setRandom();Eigen::Tensor<float, 2> C = 2.f*A + B.exp();std::cout << "A is\n\n"<< A << "\n\n";
std::cout << "B is\n\n"<< B << "\n\n";
std::cout << "C is\n\n"<< C << "\n\n";

特征张量 API 还有其他几个元素级函数,如 、 和 。此外,我们可以按如下方式使用:.exp()sqrt()log()abs()unaryExpr(fun)
auto cosine = [](float v) {return cos(v);};
Eigen::Tensor<float, 2> D = A.unaryExpr(cosine);
std::cout << "D is\n\n"<< D << "\n\n";
同样,我们可以使用:binaryExpr
auto fun = [](float a, float b) {return 2.*a + b;};
Eigen::Tensor<float, 2> E = A.binaryExpr(B, fun);
std::cout << "E is\n\n"<< E << "\n\n";

六、惰性求值和 auto 关键字
开发Eigen Tensor API的Google工程师遵循了与Eigen库顶部相同的策略。这些策略之一,也可能是最重要的策略,是如何延迟计算表达式的方式。
惰性求值策略包括延迟表达式的实际求值,以便将多个链式表达式组合到一个优化的等效表达式中。因此,优化的代码不是逐步计算多个单独的表达式,而是只计算一个表达式,旨在利用最终的整体性能。
例如,如果 和 是张量,则表达式实际上并不计算 A 和 B 的总和。实际上,该表达式会产生一个知道如何计算的特殊对象。仅当将此特殊对象分配给实际张量时,才会执行实际操作。换句话说,在下面的语句中:ABA + BA + BA + B
auto C = A + B;
C不是实际结果,而只是一个知道如何计算的计算对象(确实是一个对象)。只有当分配给张量对象(类型、、等的对象)时,才会对其进行评估以提供正确的张量值:A + BEigen::TensorCwiseBinaryOpA + BCEigen::TensorEigen::TensorMapEigen::TensorRef
Eigen::Tensor<...> T = C;
std::cout << "T is " << T << "\n\n";
当然,这对于像 这样的小型操作没有意义。但是,此行为对于长操作链非常有用,在这些操作链中,可以在实际评估之前优化计算。在简历中,作为一般准则,而不是编写这样的代码:A + B
Eigen::Tensor<...> A = ...;
Eigen::Tensor<...> B = ...;
Eigen::Tensor<...> C = B * 0.5f;
Eigen::Tensor<...> D = A + C;
Eigen::Tensor<...> E = D.sqrt();
我们应该编写这样的代码:
Eigen::Tensor<...> A = ...;
Eigen::Tensor<...> B = ...;
auto C = B * 0.5f;
auto D = A + C;
Eigen::Tensor<...> E = D.sqrt();
不同之处在于,在前者中,实际上是对象,而在后面的代码中,它们只是惰性计算操作。CDEigen::Tensor
在恢复中,最好使用惰性计算来评估长操作链,因为该链将在内部进行优化,最终导致更快的执行。
七、几何运算
几何运算会产生具有不同维度的张量,有时还会产生大小。这些操作的示例包括:、、、 和 。reshapepadshufflestridebroadcast
值得注意的是,特征张量 API 没有操作。不过,我们可以使用以下方法进行模拟:transposetransposeshuffle
auto transpose(const Eigen::Tensor<float, 2> &tensor) {Eigen::array<int, 2> dims({1, 0});return tensor.shuffle(dims);
}Eigen::Tensor<float, 2> a_tensor(3, 4);
a_tensor.setRandom();std::cout << "a_tensor is\n\n"<< a_tensor << "\n\n";
std::cout << "a_tensor transpose is\n\n"<< transpose(a_tensor) << "\n\n";
稍后,当我们讨论使用张量的示例时,我们将看到一些几何运算的示例。softmax
八、规约(reduce)
归约是一种特殊操作情况,它会导致张量的维数低于原始张量。减少的直观案例是:sum()maximum()
Eigen::Tensor<float, 3> X(5, 2, 3);
X.setRandom();std::cout << "X is\n\n"<< X << "\n\n";std::cout << "X.sum(): " << X.sum() << "\n\n";
std::cout << "X.maximum(): " << X.maximum() << "\n\n";
在上面的示例中,我们缩小了所有尺寸一次。我们还可以沿特定轴执行缩减。例如:
Eigen::array<int, 2> dims({1, 2});std::cout << "X.sum(dims): " << X.sum(dims) << "\n\n";
std::cout << "X.maximum(dims): " << X.maximum(dims) << "\n\n";

特征张量 API 具有一组预构建的归约操作,例如、、、等。如果任何预构建的操作不适合特定实现,我们可以使用提供自定义函子作为参数。prodanyallmeanreduce(dims, reducer)reducer
九、张量卷积
在前面的一个故事中,我们学习了如何仅使用普通C++和特征矩阵来实现 2D 卷积。事实上,这是必要的,因为在本征矩阵中没有内置的矩阵卷积。幸运的是,特征张量 API 有一个方便的函数来对特征张量对象执行卷积:
Eigen::Tensor<float, 4> input(1, 6, 6, 3);
input.setRandom();Eigen::Tensor<float, 2> kernel(3, 3);
kernel.setRandom();Eigen::Tensor<float, 4> output(1, 4, 4, 3);Eigen::array<int, 2> dims({1, 2});
output = input.convolve(kernel, dims);std::cout << "input:\n\n" << input << "\n\n";
std::cout << "kernel:\n\n" << kernel << "\n\n";
std::cout << "output:\n\n" << output << "\n\n";

请注意,我们可以通过控制卷积中幻灯片的尺寸来执行 2D、3D、4D 等卷积。
十、带张量的软最大值
在编程深度学习模型时,我们使用张量而不是矩阵。事实证明,矩阵可以表示一个或最多二维网格,同时我们有更高维度的数据多通道图像或批量寄存器来处理。这就是张量发挥作用的地方。
让我们考虑以下示例,其中我们有两批寄存器,每批有 4 个寄存器,每个寄存器有 3 个值:

我们可以按如下方式表示这些数据:
Eigen::Tensor<float, 3> input(2, 4, 3);
input.setValues({{{0.1, 1., -2.},{10., 2., 5.},{5., -5., 0.},{2., 3., 2.}},{{100., 1000., -500.},{3., 3., 3.},{-1, 1., -1.},{-11., -0.2, -.1}}
});std::cout << "input:\n\n" << input << "\n\n";
现在,让我们应用于此数据:softmax
Eigen::Tensor<float, 3> output = softmax(input);
std::cout << "output:\n\n" << output << "\n\n";
Softmax是一种流行的激活功能。我们在上一个故事中介绍了它的实现。现在,让我们介绍一下实现:Eigen::MatrixEigen::Tensor
#include <unsupported/Eigen/CXX11/Tensor>auto softmax(const Eigen::Tensor<float, 3> &z)
{auto dimensions = z.dimensions();int batches = dimensions.at(0);int instances_per_batch = dimensions.at(1);int instance_length = dimensions.at(2);Eigen::array<int, 1> depth_dim({2});auto z_max = z.maximum(depth_dim);Eigen::array<int, 3> reshape_dim({batches, instances_per_batch, 1});auto max_reshaped = z_max.reshape(reshape_dim);Eigen::array<int, 3> bcast({1, 1, instance_length});auto max_values = max_reshaped.broadcast(bcast);auto diff = z - max_values;auto expo = diff.exp();auto expo_sums = expo.sum(depth_dim);auto sums_reshaped = expo_sums.reshape(reshape_dim);auto sums = sums_reshaped.broadcast(bcast);auto result = expo / sums;return result;
}
此代码输出:

我们不会在这里详细介绍 Softmax。如果您需要查看Softmax算法,请不要犹豫,在Medium上再次阅读之前的故事。现在,我们只专注于了解如何使用特征张量来编码我们的深度学习模型。
首先要注意的是,该函数实际上并没有计算参数的softmax值。实际上,只挂载一个可以计算softmax的复杂对象。softmax(z)zsoftmax(z)
仅当 的结果分配给类似张量的对象时,才会评估实际值。例如,在这里:softmax(z)
Eigen::Tensor<float, 3> output = softmax(input);
在这一行之前,一切都只是softmax的计算图,希望得到优化。发生这种情况只是因为我们在 的正文中使用了关键字。因此,特征张量 API 可以优化使用更少操作的整个计算,从而改善处理和内存使用。autosoftmax(z)softmax(z)
在结束这个故事之前,我想指出和呼吁:tensor.reshape(dims)tensor.broadcast(bcast)
Eigen::array<int, 3> reshape_dim({batches, instances_per_batch, 1});
auto max_reshaped = z_max.reshape(reshape_dim);Eigen::array<int, 3> bcast({1, 1, instance_length});
auto max_values = max_reshaped.broadcast(bcast);
reshape(dims)是一种特殊的几何运算,它生成另一个张量,其大小与原始张量相同,但尺寸不同。重塑不会在张量内部更改数据的顺序。例如:
Eigen::Tensor<float, 2> X(2, 3);
X.setValues({{1,2,3},{4,5,6}});std::cout << "X is\n\n"<< X << "\n\n";std::cout << "Size of X is "<< X.size() << "\n\n";Eigen::array<int, 3> new_dims({3,1,2});
Eigen::Tensor<float, 3> Y = X.reshape(new_dims);std::cout << "Y is\n\n"<< Y << "\n\n";std::cout << "Size of Y is "<< Y.size() << "\n\n";

Note that, in this example, the size of X and Y is either 6 although they have very different geometry.
tensor.broadcast(bcast) repeats the tensor as many times as provided in the parameter for each dimension. For example:bcast
Eigen::Tensor<float, 2> Z(1,3);
Z.setValues({{1,2,3}});
Eigen::array<int, 2> bcast({4, 2});
Eigen::Tensor<float, 2> W = Z.broadcast(bcast);std::cout << "Z is\n\n"<< Z << "\n\n";
std::cout << "W is\n\n"<< W << "\n\n";

不同的 ,不会改变张量秩(即维数),而只会增加维数的大小。reshapebroadcast
十一、局限性
特征张量 API 文档引用了一些我们可以意识到的限制:
- GPU 支持经过测试并针对浮点类型进行了优化。即使我们可以声明,在使用 GPU 时也不鼓励使用非浮点张量。
Eigen::Tensor<int,...> tensor; - 默认布局(col-major)是唯一实际支持的布局。至少现在我们不应该使用行专业。
- 最大尺寸数为 250。只有在使用 C++11 兼容的编译器时才能实现此大小。
十二、结论和下一步
张量是机器学习编程的基本数据结构,使我们能够像使用常规二维矩阵一样直接地表示和处理多维数据。
在这个故事中,我们介绍了特征张量 API,并学习了如何相对轻松地使用张量。我们还了解到,特征张量 API 具有惰性评估机制,可以在内存和处理时间方面优化执行。
为了确保我们真正理解Eigen Tensor API的用法,我们介绍了一个使用张量编码Softmax的示例。
在接下来的故事中,我们将继续使用 C++ 和特征从头开始开发高性能深度学习算法,特别是使用 Eigen Tensor API。
十三、github代码
您可以在 GitHub 上的此存储库中找到此故事中使用的代码。
十四、引用
[1] 特征张量 API
[2] 特征张量模块
[3] Eigen Gitlab repository, libeigen / eigen · GitLab
[4] Charu C. Aggarwal, Neural Networks and Deep Learning: A Textbook (2018), Springer
[5] Jason Brownlee,A Gentle Introduction to Tensors for Machine Learning with NumPy
关于本系列
在本系列中,我们将学习如何仅使用普通和现代C++对必须知道的深度学习算法进行编码,例如卷积、反向传播、激活函数、优化器、深度神经网络等。

这个故事是:使用特征张量API
查看其他故事:
0 — 现代C++深度学习编程基础
1 — 在纯C++中编码 2D 卷积
2 — 使用 Lambda 的成本函数
3 — 实现梯度下降
4 — 激活函数
...更多内容即将推出。
相关文章:
现代C++中的从头开始深度学习【2/8】:张量编程
一、说明 初学者文本:此文本需要入门级编程背景和对机器学习的基本了解。张量是在深度学习算法中表示数据的主要方式。它们广泛用于在算法执行期间实现输入、输出、参数和内部状态。 在这个故事中,我们将学习如何使用特征张量 API 来开发我们的C算法。具…...
uniapp软键盘谈起遮住输入框和头部被顶起的问题解决
推荐: pages.json中配置如下可解决头部被顶起和表单被遮住的问题。 { "path": "pages/debug/protocol/tagWord", "style": { "app-plus": { "soft…...
安防监控视频汇聚EasyCVR平台的FLV视频流在VLC中无法播放的原因排查
众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上,视频监控…...
虹科新闻 | 虹科与Power-MI正式建立合作伙伴关系
近日,虹科与Power-MI正式建立合作伙伴关系,双方就工业预测性维护领域进行深入的交流与合作,未来将共同致力于为亚洲市场提供完整的、更高质量的预测性维护解决方案,解决亚洲客户的工业自动化挑战。 虹科与Power-MI都表示十分期待…...
Xamarin.Android实现加载中的效果
目录 1、说明2、代码如下2.1 图1的代码2.1.1、创建一个Activity或者Fragment,如下:2.1.2、创建Layout2.1.3、如何使用 2.2 图2的代码 4、其他补充4.1 C#与Java中的匿名类4.2 、其他知识点 5、参考资料 1、说明 在实际使用过程中,常常会用到点…...
Leetcode.1559 二维网格图中探测环
题目链接 Leetcode.1559 二维网格图中探测环 rating : 1838 题目描述 给你一个二维字符网格数组 g r i d grid grid ,大小为 m x n ,你需要检查 g r i d grid grid 中是否存在 相同值 形成的环。 一个环是一条开始和结束于同一个格子的长度 大于等于…...
阿拉伯数字转中文数字字符,最高支持千京
直接上代码 UtilityClass public class NumberFormatUtil {/** 中文 -> 数字对应关系 */private static final Map<Character, Integer> DIGIT_CHINA new HashMap<>();/** 数字 -> 中文对应关系 */private static final Map<Integer, Character> DIGI…...
Python基础--序列操作/函数
Python基础 1.序列的操作 2.函数 1. 数据类型的具体操作 1.1 序列操作--列表具体操作: #定义列表 listA [] #定义一个空列表 listB [1,2.8,"你好",listA,[1,2,3]] # 访问列表 print(listB)#查看整个列表 print(listB[2])#查看单个…...
Kafka与Zookeeper版本对应关系
文章目录 了解版本对应Kafka安装包Kafka源码包 了解 比如: kafka_2.11-1.1.1.jar包 其中2.11表示的是Scala的版本,因为Kafka服务器端代码完全由Scala语音编写。”-“后面的1.1.1表示的kafka的版本信息。遵循一个基本原则,Kafka客户端版本和服…...
Arch Linux 使用桥接模式上网
如果我们想要将虚拟机与物理主机同一网段,并且像物理机器一样被其他设备访问,则需要以桥接模式上网,这个时候,物理主机就必须配置为使用网桥上网了。 注意:这里我们使用了 NetworkManager 网络管理工具中的 nmcli 来进…...
Vue 中使用 WebWorker
目录 安装 loader 应用场景 打包时错误处理 安装 loader npm install worker-loader -D 如果直接把worker.js放到public目录下,则不需要安装loader vue.config.js const { defineConfig } require(vue/cli-service)module.exports defineConfig({transpileDe…...
财务管理系统javaweb会计账房进销存jsp源代码mysql
本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 财务管理系统javaweb java,Struts2,bootstrap,mysql,…...
企业服务器被devos勒索病毒攻击后怎么处理,devos勒索病毒如何攻击的
众所周知,科学技术是第一生产力,科学技术的发展给企业与人们的生活带来了极大变化,但随之而来的网络安全威胁也不断增加。最近,我们收到很多企业的求助,企业的计算机服务器遭到了devos勒索病毒的攻击,导致企…...
React源码解析18(2)------ FilberNode,FilberRootNode结构关系
摘要 在上一篇,我们实现了通过JSX转换为ReactElement的方法,也看到了转换后React元素的结构。但是这个React元素,并不能很清楚的表达组件之间的关系,以及属性的处理。 所以在React内部,会将所有的React元素转换为Fil…...
什么是Session?它在SQLAlchemy中扮演什么角色?
让我们先来谈谈什么是“Session”。在你逛超市或者餐厅的时候,你可能会遇到一种叫做“前台”的东西。你知道那是干什么的吗?它是用来暂存你买的东西,这样你就可以从容地结账,而不必抱着满满一购物车的商品。 数据库的“Session”…...
Java 中 Set集合常用方法
.add() 添加元素 对象名.add() 向Set集合中添加元素 (但不能添加重复元素,Set集合中不允许元素重复) Set<String> s new HashSet<String>(); // 添加数据 s.add("aaa"); s.add("bbb"); addAll(Collectio…...
(MVC)SpringBoot+Mybatis+Mapper.xml
前言:本篇博客主要对MVC架构、Mybatis工程加深下理解,前面写过一篇博客:SprintBoothtml/css/jsmybatis的demo,里面涉及到了Mybatis的应用,此篇博客主要介绍一种将sql语句写到了配置文件里的方法,即Mybatis里…...
【Linux命令行与Shell脚本编程】第十九章 正则表达式
Linux命令行与Shell脚本编程 第十九章 正则表达式 文章目录 Linux命令行与Shell脚本编程 第十九章 正则表达式九.正则表达式9.1.正则表达式基础9.1.1.正则表达式的类型9.2.定义BRE模式9.2.1.普通文本9.2.2.特殊字符 9.2.3.锚点字符锚定行首^锚定行尾$组合锚点 9.2.4.点号字符\.…...
vue exceljs 实现导出excel并设置网格线、背景色、 垂直居中、分页打印
一、 下载 exceljs pnpm install exceljs二、 页面中使用 // 导出 exportExcelexportToExcel() {this.$confirm("此操作将导出excel文件, 是否继续?", "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "wa…...
TC358774/5显示桥接(MIPI DSI到LVDS)
东芝TC358774/5显示桥针对使用带有MIPI DSI(显示串行接口)连接的主机处理器的手持设备进行了优化。tc358774 /5作为协议桥接,使视频数据流从主机处理器链接到驱动LVDS显示面板。tc358774 /5桥接器可以配置为多达4通道MIPI DSI,每通道数据速率高达1 Gbps&…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
沙箱虚拟化技术虚拟机容器之间的关系详解
问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
