【caffe解读】 caffe从数学公式到代码实现3-shape相关类

10行Python代码实现令陶哲轩惊叹的数学公式! 最近陶神的一篇小论文引起了广泛关注,我们也来跟风吃个瓜。 为了代码实现,有必要先来解读一下数学公式。陶神的研究领域需要比较深的数学背景知识,一般人是很难看懂的。而这篇里的公式涉及的数学知识大家在本科时都已经学过了,所以只要稍作展开就能看懂哦。 论文里的证明比较简练,为了让更多的吃瓜群众看明白,本文按照陶神论文里的思路将证明作了详细展开。如果不想啃公式,那就直接跳到后面看代码吧。 理论部分 ... 阅读详情

文章首发于微信公众号《与有三学AI》

[caffe解读] caffe从数学公式到代码实现3-shape相关类

接着上一篇说,本篇开始读layers下面的一些与blob shape有关的layer,比如flatten_layer.cpp等,具体包括的在下面;

flatten_layer.cpp
slice_layer.cpp
split_layer.cpp
tile_layer.cpp
concat_layer.cpp
reduction_layer.cpp
eltwise_layer.cpp
crop_layer.cpp
pooling_layer.cpp
scale_layer.cpp

conv与deconv虽然也与shape有关,但是由于比较复杂,我们以后专门留一篇来说。下面这些层,如果你没有仔细读过源码,那么建议你来读一读,因为有很多并没有想象中那么简单。

 

01 flatten_layer.cpp

Flatten layer的作用是把一个维度为n * c * h * w的输入转化为一个维度为 n* (c*h*w)的向量输出,虽然在我们看来不一样,但是在blob看来,输入和输出的数据存储是没有差异的,只是记录的shape信息不同。所以forward和backward只是数据拷贝

template <typename Dtype>
void FlattenLayer<Dtype>::Forward_cpu(constvector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) { top[0]->ShareData(*bottom[0]);
}

template <typename Dtype>
void FlattenLayer<Dtype>::Backward_cpu(constvector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, constvector<Blob<Dtype>*>& bottom) {
bottom[0]->ShareDiff(*top[0]);
}

 

02 slice_layer.cpp

Slice layer 的作用是将bottom按照需要分解成多个tops,它的定义如下:

message SliceParameter {
// By default, SliceLayer
concatenates blobs along the "channels" axis (1).
optional int32 axis = 3 [default = 1];
repeated uint32 slice_point = 2;
// DEPRECATED: alias for "axis" -- does not supportnegative indexing.
optional uint32 slice_dim = 1 [default = 1];
}

默认axis是1,也就是blob的第1个维度,即channel通道,这也是我经常使用的,一般用于有多种label时分离label。

前向反向时小心计算好offset就行,有兴趣可以去看。

 

03 split_layer.cpp

它的作用是将输入复制多份。

Forward: 在前向的时候,top[i]=bottom[0],直接赋值。

template <typename Dtype>
void SplitLayer<Dtype>::Forward_cpu(constvector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
for (int i = 0; i < top.size(); ++i) {
  top[i]->ShareData(*bottom[0]);
}
}

Backward: 在反向的时候,需要将所有top的diff叠加起来。

template <typename Dtype>
void SplitLayer<Dtype>::Backward_cpu(constvector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, constvector<Blob<Dtype>*>& bottom) {
if (!propagate_down[0]) { return; }
if (top.size() == 1) { caffe_copy(count_, top[0]->cpu_diff(), bottom[0]->mutable_cpu_diff());
return;
}
caffe_add(count_, top[0]->cpu_diff(), top[1]->cpu_diff(),
bottom[0]->mutable_cpu_diff());
// Add remaining top blob diffs.
for (int i = 2; i < top.size(); ++i) {
const Dtype* top_diff = top[i]->cpu_diff();
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
caffe_axpy(count_, Dtype(1.), top_diff, bottom_diff);
}
}

 

04 tile_layer.cpp

数学定义:

将数据按照某个维度扩大n倍,看下面forward源码,将bottom_data的前inner_dim_个数据复制了tiles份,反向时将对应diff累加回去即可。

void TileLayer<Dtype>::Forward_cpu(

    const vector<Blob<Dtype>*>& bottom, constvector<Blob<Dtype>*>& top) {

  const Dtype* bottom_data = bottom[0]->cpu_data();

  Dtype* top_data = top[0]->mutable_cpu_data();

  for (int i = 0; i < outer_dim_; ++i) {

    for (int t = 0; t < tiles_; ++t) {

      caffe_copy(inner_dim_, bottom_data, top_data);

      top_data += inner_dim_;

    }

    bottom_data += inner_dim_;

  }

}

 

05 concat_layer.cpp

与slice_layer是反向操作,将多个bottom blob合并成一个top_data,forward,backward计算好index就行。

 

06 reduction_layer.cpp

顾名思义,这是一个降维的层。

数学定义:

message ReductionParameter {
enum ReductionOp {
SUM = 1;
ASUM = 2;
SUMSQ = 3;
MEAN = 4;
}
optional ReductionOp operation = 1 [default =SUM]; // reduction operation The first axis to reduceto a scalar -- may be negative to index from the end(e.g., -1 for the last axis). (Currently, only reductionalong ALL "tail" axes is supported;
reduction of axis M through N, where N < num_axes- 1, is unsupported.)
Suppose we have an n-axis bottom Blob with shape:
(d0, d1, d2, ..., d(m-1), dm, d(m+1), ..., d(n-1)).
If axis == m, the output Blob will have shape  (d0,d1, d2, ..., d(m-1)),
and the ReductionOp operation is performed (d0 *d1 * d2 * ... * d(m-1))
times, each including (dm * d(m+1) * ... * d(n-1))individual data.
If axis == 0 (the default), the output Blob always hasthe empty shape
(count 1), performing reduction across the entireinput often useful for creating new loss functions.
optional int32 axis = 2 [default = 0];
optional float coeff = 3 [default = 1.0]; // coefficientfor output
}

从上面可以看出,reduct有4类操作,sum,mean,asum,sumsq,分别是求和,求绝对值和,求平方和与平均。它会从axis这个维度开始去降维,比如当axis=0,就是从第0维开始将所有blob降维,最终会得到一个标量数,常用于loss。

在reshape函数中可以看到,

axis_ = bottom[0]->CanonicalAxisIndex(this->layer_param_.reduction_param().axis());
vector<int> top_shape(bottom[0]->shape().begin(),bottom[0]->shape().begin() + axis_);
top[0]->Reshape(top_shape);
num_ = bottom[0]->count(0, axis_);
dim_ = bottom[0]->count(axis_);
CHECK_EQ(num_, top[0]->count());

通过reduction_param().axis())设置维度之后,top[0]的元素数目就是num_ =
bottom[0]->count(0, axis_);我们假设输入blob是10*3*224*224,如果axis=0,那么top[0]=10*1*1*1;如果axis=1,那么top[0]=10*3*1*1,以此类推。

Forward和Backward对应这4个操作去看代码即可,只要知道反向的时候,top的每一个元素的梯度会反传给bottom的多个元素。

 

07 eltwise_layer.cpp

eltwise是一个有多个bottom输入,一个top输出的layer,对逐个的元素进行操作,所bottom[i]和top[j]的大小都是相等的。Eltwise参数有相乘PROB,相加SUM,求MAX。对于SUM操作,该层定义了 coeff 参数,用于调整权重。 对于PROB操作,设定了stable_prod_grad #[default = true ] 来选择是否渐进较慢的梯度计算方法,forward过程不需要说太多,而对于backward,有必要说一下。下面举prob操作的例子;

我们看相应函数,这只是内循环,实际上还有外循环。

case EltwiseParameter_EltwiseOp_PROD:
if (stable_prod_grad_) { bool initialized = false;
for (int j = 0; j < bottom.size(); ++j) {
if (i == j) { continue; }
if (!initialized) { 
caffe_copy(count, bottom[j]->cpu_data(),bottom_diff);
initialized = true;
} else {
caffe_mul(count, bottom[j]->cpu_data(), bottom_diff,
bottom_diff);
}
}
} else {
caffe_div(count, top_data, bottom_data,bottom_diff);
}
caffe_mul(count, bottom_diff, top_diff, bottom_diff);

当stable_prod_grad = false时,直接对应了上面的式top_data/bottom_data*bottom_diff,但是如果stable_prod_grad = true,差异在哪呢?反正我是没看出啥区别,只是为true时没有利用已经计算好的结果,计算更慢了。

 

08 crop_layer.cpp

crop layer改变blob的第2,3个维度,而不是改变前两个维度,也没有复杂的数学操作,所以只需要记录下offset即可,感兴趣还是去看源码。

 

09 pooling_layer.cpp

pooling layer想必大家都很熟悉了,caffe官方的有MAX,MEAN两种,还保留了一种random的没有实现。 Max和Mean的区别会在什么地方呢?主要就是max会存在一个mask,因为它要记录对top有贡献的那个元素,在梯度反传的时候,也只会反传到1个元素,而mean则会反传到r*r个元素,r就是滤波的半径。

其他的倒是没有需要特别注意的地方,主要就是bottom到top的index计算,细节处小心即可。

 

10 bnll_layer.cpp

数学定义:

就这么多。

 

11 scale_layer.cpp

scale这个layer绝对比你想象中复杂多。我们通常以为是这样就完了

其中a是一个标量,x是一个矢量,在caffe中就是blob,但是实际上a也可以是blob,它可以有如下尺寸,见scale参数的定义:

message ScaleParameter {
// The first axis of bottom[0] (the first input Blob)along which to apply, bottom[1] (the second inputBlob). May be negative to index from the end (e.g.,-1 for the last
axis).
// For example, if bottom[0] is 4D with shape100x3x40x60, the output top[0] will have the sameshape, and bottom[1] may have any of the followingshapes (for the given value of axis): 
(axis == 0 == -4) 100; 100x3; 100x3x40;100x3x40x60
(axis == 1 == -3) 3; 3x40; 3x40x60
(axis == 2 == -2) 40; 40x60
(axis == 3 == -1) 60
// Furthermore, bottom[1] may have the emptyshape (regardless of the value of "axis") a scalarmultiplier.
optional int32 axis = 1 [default = 1];
// (num_axes is ignored unless just one bottom isgiven and the scale is a learned parameter of thelayer. Otherwise, num_axes is determined by thenumber of axes by the second bottom.)
// The number of axes of the input (bottom[0])covered by the scale
// parameter, or -1 to cover all axes of bottom[0]starting from `axis`.
// Set num_axes := 0, to
multiply with a zero-axis Blob: a scalar.
optional int32 num_axes = 2 [default = 1];
// (filler is ignored unless just one bottom is givenand the scale is
// a learned parameter of the layer.)
// The initialization for the learned scale parameter.
// Default is the unit (1) initialization, resulting in theScaleLayer
// initially performing the identity operation.
optional FillerParameter filler = 3;

// Whether to also learn a bias (equivalent to aScaleLayer+BiasLayer, but
// may be more efficient). Initialized with bias_filler(defaults to 0).
optional bool bias_term = 4 [default = false];
optional FillerParameter bias_filler = 5;
}

从上面我们可以知道这些信息;

(1) scale_layer是输入输出可以都是1个,但是,输入可以是两个,也就是bottom[1]是scale,当没有bottom[1]时,就是通过一个标量参数来实现scale。

(2) scale可以有多种尺寸。从1维到4维。

上面举了例子,当输入x是100x3x40x60,scale blob可以是100; 100x3;
100x3x40; 100x3x40x60这几种尺寸,所以在forward,backward的时候,需要对上尺寸。

这一节看起来比较乱,就当读书笔记吧,只是有很多细节,真的需要自己去抠才知道坑在哪。

 

同时,在我的知乎专栏也会开始同步更新这个模块,欢迎来交流

https://zhuanlan.zhihu.com/c_151876233

注:部分图片来自网络

—END—

打一个小广告,我在gitchat开设了一些课程和chat,欢迎交流。

感谢各位看官的耐心阅读,不足之处希望多多指教。后续内容将会不定期奉上,欢迎大家关注有三公众号 有三AI!

 

 

实现数学公式C(n,m)的java程序 该代码实现功能为数学中的C(n,m),n为下标,m为上标。 立即下载

相关推荐

数学公式源代码

类似于数学公式编辑器的源码,大家可参考此方式开发数据公式编辑器。

caffe可视化各个层

caffe可视化各个层

奔跑的小白 1007

【Arc GIS 零基础教学】土地利用类型图的绘制方法

​Hi,我是胡学长!今天教大家怎么利用Arc GIS 绘制土地利用类型图。土地利用类型图是表达土地资源的利用现状、地域差异和分类的专题地图。它是研究土地利用的重要工具和基础资料,同时也是土地利用调查研究的主要成果之一。在编制土地利用图的基础上,对当前利用的合理程度和存在的问题、进一步利用的潜力、合理利用的方向和途径,进行综合分析和评价。因此,土地利用图是调整土地利用结构,因地制宜进行农业、工矿业和交通布局、城镇建设、区域规划、国土整治、农业区划等的一项重要科学依据。......

一起来交流学习吧 1万+

caffe 打印出Forward 函数输入输出的shape

void MyLossLayer<Ftype, Btype>::Forward_cpu(const vector<Blob*>& bottom, const vector<Blob*>& top) { // bottom[0]: "depth" 1x1x36x64 // bottom[1]: "data" for loss resha...

无左无右的博客 494

点云处理工具——pclpy安装

pclpy介绍及详细安装过程 文章目录pclpy简介下载:安装测试 pclpy简介 pclpy是点云库(PCL)的Python绑定。使用CppHeaderParser和pybind11从头文件生成。 这个库正在积极开发中,api可能会发生变化。所包含的模块确实可以工作,但测试还不完整。目前只支持Windows和python 3.6 x64。 许多其他python库尝试绑定PCL。最流行的是python-pcl,它使用Cython。虽然Cython非常强大,但绑定c++模板并不是它的强项(PCL大量使用模.

图像处理、SLAM、PCL、OpenCV爱好者 1万+

COMSOL电弧放电仿真:四场耦合与磁流体建模实战指南

多物理场耦合仿真是现代工程中模拟复杂物理过程的重要手段。电弧本质上是电场、磁场、流场与温度场四场相互耦合的非线性系统,其行为由磁流体力学(MHD)方程描述。通过COMSOL等工具构建电弧模型,可准确预测温度分布、流场结构和电极烧蚀,为开关电器设计、等离子体应用等提供数据支撑。在高压电器开断、电弧切割、激光打孔等工程问题中,高效求解MHD耦合模型是关键。然而,电弧仿真涉及物性参数强非线性、边界条件设置、求解器配置以及移动网格处理等挑战。以二维轴对称电弧模型为例,系统梳理磁流体方程落地、多物理场耦合节点配置、收

weixin_30047059的博客 136

常用数学公式的编程表达

今天把常用数学公式用C/C++表达组织一下,以便日后使用。排列组合数 随机输出任意一个全排列排列组合数 先来看看排列组合数的公式: Amn=n(n−1)…(n−m+1)=n!(n−m)!A_n^m = n(n-1) \dots (n-m+1) = \frac{n!}{(n-m)!} Cmn=Amnm!=n!m!(n−m)!=Cn−mnC_n^m = \frac{A_n^m}{m!} = \fr

对酒当歌,人生几何。譬如朝露,去日苦多 3545

【补充:脑电绘图教程 2】如何手动去除ICA伪迹分量

ICA可以将脑电信号分解为多个IC分量,由每个分量的平均功率地形图可以看出哪些分量属于伪迹,去除伪迹分量后,即可得到“干净”的脑电信号。本教程主要总结如何人工识别出ICA伪迹分量,不涉及ICA的原理讲解。本教程的主要知识来源是ICLabel官网ICLabel致力于自动识别IC分量属于哪种伪迹,并标记伪迹占比,可以在eeglabel中很方便地使用。ICLabel给出了几种常见伪迹的特点,本教程主要是根据ICLabel给出伪迹特点,对自己的数据集手动去除伪迹,以更深入了解自己数据集的质量和伪迹特点。

qq_40735196的博客 4232

数学公式的python代码实现

1、欧氏距离公式,并排序 diffMat = tile(X, (dataSize, 1)) -dataSet sqDiffMat = diffMat**2 sqDistances = sqDiffMat.sum(axis = 1) distances = sqDistances**0.5 SortedDistIndicies =distances.argsort()

qq_32979147的博客 3620

YOLOv11半监督学习深度解析:基于FixMatch改进的先进目标检测框架

本文探讨了半监督学习与YOLOv11结合的创新方法。针对目标检测中半监督学习面临的伪标签质量不稳定、特征一致性难保证等挑战,研究提出改进版FixMatch框架。通过分析YOLOv11架构特性(如多尺度特征提取、双标签分配策略等)与半监督学习的适配性,设计了三项关键技术改进:空间感知阈值策略、多任务解耦处理和记忆增强机制。该方法在少量标注数据下可实现接近全监督的性能,显著降低工业检测等场景的数据标注成本。

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 2087

彩票的未来:基于人工智能与大数据的创新预测模型

随着人工智能(AI)和大数据技术的迅猛发展,彩票行业正面临前所未有的变革。本文将探索如何通过结合深度学习、机器学习和数据挖掘技术,构建先进的预测模型,以提高彩票开奖结果的预测准确性。通过分析历史数据、应用现代化的计算技术,我们提出了一种创新的彩票预测软件架构,旨在推动彩票行业向着更加智能和高效的方向发展。

Sapphire521的博客 1444

[caffe解读] caffe从数学公式到代码实现3-shape相关类

接着上一篇说,本篇开始读layers下面的一些与blob shape有关的layer,比如flatten_layer.cpp等,具体包括的在下面;flatten_laye...

hacker_long的专栏 470

31、摩擦学中的材料选择

本博文深入探讨了摩擦学应用中材料选择的重要性,分析了材料的机械、物理和化学属性对摩擦学性能的影响。同时介绍了材料匹配原则、处理方法及表面涂层技术,以优化耐磨性和抗腐蚀性。通过实际案例和评估方法,为读者提供了系统化的材料选择流程和决策依据。

juice的博客 249

caffe data层_【caffe教程3】caffe中的数据shape相关类

本文首发于公众号《有三AI》[caffe解读] caffe从数学公式到代码实现3-shape相关类​mp.weixin.qq.com接着上一篇说,本篇开始读layers下面的一些与blob shape有关的layer,比如flatten_layer.cpp等,具体包括的在下面;flatten_layer.cpp slice_layer.cpp split_layer.cpp tile_layer....

weixin_42157166的博客 254

DDR5 输入时钟抖动规范全解析!技术宅必看

简单说,时钟抖动就是时钟信号边沿的“颤抖”——信号本来应该在某个固定时间跳变,但实际时刻可能会前后波动。这种波动可能是随机的,也可能是确定性的。随机抖动(Random Jitter, Rj):符合高斯分布,像是你家猫咪不经意的晃动。确定性抖动(Deterministic Jitter, Dj):受限于特定周期或模式,像是邻居装修时的规律敲击声。⚠️ 注意,如果时钟抖动超出规格,DDR5可能无法正确接收或发送数据,内存错误、系统崩溃可就跟着来了!

weixin_43199439的博客 394

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

[caffe解读] caffe从数学公式到代码实现5-caffe中的卷积

今天要讲的就是跟卷积相关的一些layer了im2col_layer.cppbase_conv_layer.cppconv_layer.cppdeconv_layer.cp...

hacker_long的专栏 516

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【好专栏】深度学习框架不会用?12大主流深度学习框架快速入门,你值得拥有!...

欢迎来到【好专栏】,本次我们给大家推荐一个专栏,名为《12 大深度学习开源框架快速入门》,这个专栏为介绍了深度学习的 12 大主流框架,附带实践案例,能够让读者快速掌握深度学习框架的使用...

Leong Blog 516

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

caffe中deploy文件中的input_shape的dim:10dim:3dim:32dim:32

deploy 文件中的数据层更为简单 layer { name: "data" type: "Input" top: "data" input_param { shape: { dim: 10 dim: 3 dim: 32 dim: 32 } } } 注:shape: { dim: 10 dim: 3 dim: 32 dim: 32 }代表含义:

爱吃鱼的猫 1万+

Caffe源码:math_functions 解析

caffe源码中的math_functions的具体函数功能分析

ShaowenLin 858

[caffe解读] caffe从数学公式到代码实现1-导论

新板块说明今天开一个新板块,目标是死磕现有的几大机器学习框架的代码,给想入门的小白们一些帮助。作为一个在图像行业战斗了几年的程序员,深知入门一个框架,和真的能用好一个框架...

hacker_long的专栏 543

[caffe解读] caffe从数学公式到代码实现4-认识caffe自带的7大loss

本节说caffe中常见loss的推导,具体包含下面的cpp。multinomial_logistic_loss_layer.cpp01multinomial_logist...

hacker_long的专栏 513

从零开始搭建神经网络(二)数学公式及代码实现

1. 激活函数激活函数这里选择最简单的sigmod()函数

学如不及,犹恐失之 7478

Caffe框架初步理解

当前最火无非就是深度学习了。搞了大半年的机器人相关视觉东西,用的最多的也是Caffe。看网上有形形色色的Deeplearning的net,但是他们背后到底是什么样子的?这么说吧!这里我们把Caffe比作一个汽车。汽车怎么组成的?各种零件先组装成轮胎、发动机、车座、车外部框。。。然后这些组成的部件组成一个完成的车。那么有的人要问了这和深度学习框架Caffe有个毛的联系啊???!!!(ps:黑人问号脸)

不积跬步,无以致千里;不积小流,无以成江海 1万+

caffe代码阅读10:Caffe中卷积的实现细节(涉及到BaseConvolutionLayer、ConvolutionLayer、im2col等)-2016.4.3

本文介绍了Caffe中的卷积层的实现,以及卷积实现的时候所使用的im2col的实现,其中重点介绍了im2col的实现原理。

一亩半分地 1万+

10 行 Python 代码实现令陶哲轩惊叹的数学公式

最近,陶神的一篇小论文引起了广泛关注,我们也来跟风吃个瓜。 为了代码实现,有必要先来解读一下数学公式。陶神的研究领域需要比较深的数学背景知识,一般人是很难看懂的。而这篇里的公式涉及的数学知识大家在本科时都已经学过了,所以只要稍作展开就能看懂哦。 论文里的证明比较简练,为了让更多的吃瓜群众看明白,本文按照陶神论文里的思路将证明作了详细展开。如果不想啃公式,那就直接跳到后面看代码吧。 理论证明 我们...

coolend的专栏 591

【pytorch速成】Pytorch图像分类从模型自定义到测试

文章首发于微信公众号《与有三学AI》 【pytorch速成】Pytorch图像分类从模型自定义到测试 前面已跟大家介绍了Caffe和TensorFlow,链接如下。 【caffe速成】caffe图像分类从模型自定义到测试 【tensorflow速成】Tensorflow图像分类从模型自定义到测试 今天说说Pytorch。 作者&编辑|言有三 01 什么是 Py...

hacker_long的专栏 1万+
上一篇: 【caffe解读】 caffe从数学公式到代码实现4-认识caffe自带的7大loss
下一篇: 【求职必备】学深度学习,这些公众号你有关注吗?
言有三
言有三 领域专家: 人工智能技术领域 领域专家: 人工智能技术领域
博客等级 码龄15年 3635粉丝 976原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

言有三

三人行必有AI

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

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

打赏作者

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

抵扣说明:

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

余额充值