本文使用 Zhihu On VSCode 创作并发布

向量化编程对于用过Matlab或者numpy做过稍微复杂一点的数值运算的人来说并不陌生,甚至说刚入门Matlab或者numpy的小白都知道要用z = x .* y来代替一个for k = 1 : N {z(k) = x(k) * y(k);},进阶一点的人也明白迭代算法最好把更新函数写成向量化的形式。以前觉得向量化编程就是个小技巧,对着入门教程中那些梯度下降例子无脑抄几遍就能掌握向量化编程的主要精髓,直到前两天因为要对一个二级劝退学科的实验数据后处理程序向量化,才体会到要掌握向量化编程思想还是得动脑子。。。

实验数据后处理需求

我有在

个settings
下分别测得的数据
,其中每组数据
是一个随机离散时间序列
,其中
。然而理论合作者那边要求获得的时间序列要满足每个数据点所对应的测量setting必须是从那
个settings中按照给定概率分布随机抽取的,那我必须生成一条随机setting序列
,其中
,然后用我那测得的
组数据按照这个setting序列生成一条新的随机序列:也就是说假设这个setting序列里面有
个是
,那么我就用数据
中的前
个点往对应的位置填充,以此类推,那么生成的随机序列
就满足了理论合作者的要求。

在获得了这个随机序列

以后,还需要用它来获得两条新序列
用来实验结果的曲线图。第一条序列是这样的:对于第
个点,先求得前
个数据点的平均值
,然后求
,其中
是KL散度函数,
是一个固定不变的值。第二条序列是这样的:对于第
个点,同样先求得前
个数据点的平均值
,然后求

其中

是KL散度
在固定
后的逆函数
(这种条件下的逆函数是存在的,因为单调,只不过需要用数值方法求解)。

这还不行,单次实验结果方差太大,需要做

实验,也就是说上面过程重复个
次。

向量化过程

这个实验数据后处理程序初始版本还真的是严格按照上面过程编写的:每次产生一个随机setting,然后从原始数据抽取数据点填充,计算

,再计算
。当时这个程序写出来是为了仿真一个低维高保真度的情况,
不需要太大(5000左右吧,
取50),跑的时间还是可以接受的(不到一分钟吧),毕竟就画个图给老板看。结果实验数据维度高了保真度低了,
得取到50000左右,结果运行时间直接飙升到十几分钟,但我还要对着跑出来图debug,这来回几下也太费时间了。

我对源程序向量化的过程主要用到的技巧是数组的逻辑寻址,然后对满足不同逻辑的数据点分别进行不同的运算。首先生成指定分布的随机序列是不需要一个一个来的,假设我在

个settings上的概率分布为
,那这个随机序列生成函数可以这么写:
function randseq = getRandSeq(len, probs)
cutpoints = cumsum(probs); % 给不同结果划分区间
randseq = rand(1, len);
idx = randseq < cutpoints(1);   % 找到落在第一个区间的所有点的下标
randseq(idx) = 2;  % 给第一个区间上的点赋予对应的值
for k = 2 : max(size(probs))   % 对后面K-1个区间重复上面过程idx = randseq < cutpoints(k) & randseq >= cutpoints(k-1);randseq(idx) = k+1;
end
randseq = randseq - 1;
end

随机setting序列生成之后,用原始数据填充过程也是可以向量化的:

results = zeros(1, N);       % 给生成的随机序列预分配空间
% 生成随机setting序列
setting_seq = getRandSeq(N, probs);
% 用原始数据填充setting序列
for k = 1 : setting_num    idx = (setting_seq == k);    % 在setting序列中找出所有S_k对应的下标results(idx) = raw_data{k}(1:sum(idx));   % 用对应原始数据往对应位置填充
end

计算

序列同样可以向量化:
results = cumsum(results) ./ (1:N);  % 直接在原地更新,不新建数组了

计算KL散度的函数

也给它向量化了:
function D = klDiv(p1, p0)
% 计算两个伯努利分布之间的KL散度
% p1和p2要么是两个长度相等的array,要么其中一个是数,另一个是array% 检查输入的范围
if sum(p0 < 0) > 0 || sum(p0 > 1) > 0 || sum(p1 > 1) > 0 || sum(p1 < 0) > 0error("illegal input.")
end
if max(size(p0)) == 1 % p1为array, p0为数值D = zeros(size(p1));  % 预分配内存indices0 = (p1 <= p0);  % 特殊情况0点下标indices1 = (p1 == 1);  % 特殊情况1点下标indices2 = (p1 > p0) & (p1 < 1);   % 正常情况点下标D(indices0) = 0;D(indices1) = log(1 / p0);D(indices2) = p1(indices2) .* log(p1(indices2) / p0) +...(1 - p1(indices2)) .* log((1 - p1(indices2)) / (1 - p0));
elseif max(size(p1)) == 1 % p0为array, p1为数值D = zeros(size(p0));  % 预分配内存if p1 == 1D(1:end) = log(1 ./ p0);elseindices0 = (p0 >= p1); % 特殊情况0点下标indices1 = (p0 < p1); % 正常情况点下标D(indices0) = 0;D(indices1) = p1 * log(p1 ./ p0) +...(1 - p1) * log((1 - p1) ./ (1 - p0));end
elseif max(size(p1)) == max(size(p0)) % p0和p1为等长度的arrayD = zeros(size(p1));  % 预分配内存indices0 = (p1 <= p0);  % 特殊情况0点下标indices1 = (p1 == 1);  % 特殊情况1点下标indices2 = (p1 > p0) & (p1 < 1);   % 正常情况点下标D(indices0) = 0;D(indices1) = log(1 ./ p0(indices1));D(indices2) = p1(indices2) .* log(p1(indices2) ./ p0(indices2)) +...(1 - p1(indices2)) .* log((1 - p1(indices2)) ./ (1 - p0(indices3)));
elseerror("illegal input.")
end
end

那么计算

序列就可以一口气完成了:
Delta = exp(-klDiv(results, p0) .* (1:N));

最头疼的是

序列的计算,因为每个点都需要用数值方法求解(我这用的是二分法,因为函数是单调的),当时我的思路是能不能想出什么近似方法把KL散度的逆函数显式表达出来,这样就可以向量化计算了,结果没想出来。。。后来就直接把二分法这个过程给向量化了:
function x = invKLDiv(ps, y)
threshold = 1e-4;  % 计算精度
left = zeros(size(ps));
right = ps;
x = (left + right) / 2;
res = klDiv(ps, x) - y;
idx = (right - left) > threshold;   % 找出精度仍然不达标的所有下标
idx1 = (res > 0) & idx; % 找出idx中需要更新区间左边界的所有下标
idx2 = (res <= 0) & idx;   % % 找出idx中需要更新区间右边界的所有下标
while sum(idx) > 0   % 循环直到所有点都达到精度要求left(idx1) = x(idx1);right(idx2) = x(idx2);x(idx) = (left(idx) + right(idx)) / 2;res(idx) = klDiv(ps(idx), x(idx)) - y(idx);idx = (right - left) > threshold;idx1 = (res > 0) & idx;idx2 = (res <= 0) & idx;
end
end

那现在

序列也可以一口气计算了:
eps_A = 16 * (1 - invKLDiv(results, -log(delta)./ (1:N))) / 3;

最后就是把那

实验数据后处理给向量化了,很简单,就是把上面生成的随机setting序列的长度从
拓展到
就是了,最后整个过程长这样:
% start post-processing the raw data
one_to_N = 1 : N;
one_to_N_M = repmat(one_to_N, 1, M);
results = zeros(1, N*M);       % test results sequence
% generate a random setting sequence
setting_seq = getRandSeq(N*M, probs);
% fill the results sequence by raw data according to setting_seq
for k = 1 : setting_num    idx = (setting_seq == k);results(idx) = raw_data{k}(1:sum(idx));
end
% compute the significance and infidelity sequence
for k = 1 : Mresults((k-1)*N+1 : k*N) = cumsum(results((k-1)*N+1 : k*N)) ./ one_to_N;
end
Delta = exp(-klDiv(results, p0) .* one_to_N_M);
eps_A = d * (1 - invKLDiv(results, -log(delta) ./ one_to_N_M)) / ((d + 1) * nu);
% store the M i.i.d. exp data
for k = 1 : Mexp_data{4*(k - 1) + 1} = results(k*N);exp_data{4*(k - 1) + 2} = Delta((k-1)*N+1 : k*N);exp_data{4*(k - 1) + 3} = eps_A((k-1)*N+1 : k*N);exp_data{4*(k - 1) + 4} = results((k-1)*N+1 : k*N);
end

整个运行时常缩短为11秒,比之前的十几分钟快了60多倍,爽爆了。

向量化计算cell_Matlab向量化编程在二级劝退学科中的一个应用例子相关推荐

  1. matlab repmat函数_Matlab向量化编程在二级劝退学科中的一个应用例子

    本文使用 Zhihu On VSCode 创作并发布 向量化编程对于用过Matlab或者numpy做过稍微复杂一点的数值运算的人来说并不陌生,甚至说刚入门Matlab或者numpy的小白都知道要用z ...

  2. C++:计算有n种情况a,b,c中任意一个使之成为最大数需要额外加多少

    题目概述: 有n种情况,向三人投票,总票数分别为a,b,c.如果使其中一人票数超过其他人,则需要多少票. 编程: #include< iostream> using namespace s ...

  3. C语言编程 5.7 从键盘中输入一个英文字母,如果它是大写则转化为小写。如果它是小写则转化为大写,并将其ASCll码显示到屏幕上。

    方法一: #include <stdio.h> void main() {         char ch; printf("请输入字母"); ch=getchar() ...

  4. PyTorch与向量化计算

    还是先认错啊  只为自己好加标签 自己看方便~~ 向量化计算是一种特殊的并行计算方式.程序在同一时间内只执行一个操作,而并行计算可以在同一时间内执行多个操作.向量化计算是指对不同的数据执行同样的一个或 ...

  5. 向量化计算cell_吴恩达老师课程笔记系列第24节-Octave教程之向量化和作业(6)

    5.1 向量化 参考视频: 5 - 6 - Vectorization (14 min).mkv 在这段视频中,我将介绍有关向量化的内容,无论你是用 Octave,还是别的语言,比如MATLAB 或者 ...

  6. [转载] Python之NumPy基础:数组与向量化计算

    参考链接: Python中的numpy.tanh 本博客为<利用Python进行数据分析>的读书笔记,请勿转载用于其他商业用途. 文章目录 1. NumPy ndarray:多维数组对象1 ...

  7. Python之NumPy基础:数组与向量化计算

    本博客为<利用Python进行数据分析>的读书笔记,请勿转载用于其他商业用途. 文章目录 1. NumPy ndarray:多维数组对象 1.1 生成ndarray 1.2 ndarray ...

  8. 计算平均指令时间_为什么向量化计算(vectorization)会这么快?

    背景 在一次iOS程序的性能测试过程中,我们发现一个自己写的argmax函数的耗时严重超出预期--这个预期是基于平常神经网络中的argmax op的速度得到的直接感官体验.不过这也不算意外,第一个版本 ...

  9. C++性能优化系列——3D高斯核卷积计算(二)FMA向量化计算一维卷积

    高斯核的卷积计算是可分离的,即高斯核的每一个维度可以分开处理.因此,一维卷积计算成为了实现3D高斯卷积的基础.一维卷积计算的性能直接影响了整个程序的性能.本篇将实现一维卷积功能,同时引出ICC编译器对 ...

  10. R语言学习系列之向量化计算

    ##R语言学习系列之向量化计算 本文主要讲解R语言向量化计算的原理及方法,希望对初学者能够提供帮助. ##一.向量化 什么是向量化计算呢?其实你可以简单的理解成这样:当我们在使用函数或者定义函数的时候 ...

最新文章

  1. 安装完python后、还需要安装什么-安装python后
  2. Android 监听手机GPS打开状态
  3. Wechat的支付逻辑流程
  4. 字节跳动只剩下小米这一个朋友了
  5. 前端学习(2755):配置tabber其他属性
  6. Github作为maven私服仓库用
  7. linux下的C语言开发(网络编程)
  8. 模拟admin组件自己开发stark组件之创建篇
  9. TensorFlow学习笔记——TensorFlow入门
  10. H3CSE路由-配置OSPF高级
  11. 在windows10上安装texlive的参考文档
  12. 必收藏的九大塑料注塑成型技术及其特点
  13. 让gentoo安装不再难
  14. H264码流中NALU sps pps IDR帧的理解
  15. 如何实现1分钟写一个API接口
  16. 集福啦!你想要的“福”这里都有~
  17. 学术英语/专业英语——基本结构及特点
  18. 北京市重点区域5G网络实测分析
  19. JavaWeb学习之BS/CS架构及tomcat容器项目部署
  20. w7设置双显示器_win7如何设置双显示器

热门文章

  1. 令人震惊的电子邮件归档调查
  2. 因打印日志而引发的故障
  3. Python使用requests发送post请求的三种方式
  4. java excel导出(基于注解)
  5. 高反差保留滤镜学习OpenCV:滤镜系列(11)——高反差保留
  6. ireport +jasperreport 中文不能显示
  7. 更改VS.NET 默认SCM Provider的方法
  8. 人生的一切问题,归根结底就是这三点:无知!恐惧!延迟!
  9. 190331每日一句
  10. 视频方向的变换by ppt