scratch lenet(5): 快速生成随机数的C语言实现

news/2024/11/26 2:04:23/

文章目录

    • 1. 目的
    • 2. 使用 `rand()` 的正确姿势
    • 3. 使用 TAOCP 公式
      • 3.1 实现
      • 3.2 使用
    • 4. 随机数:用于 Xavier Glorot 初始化
      • 4.1 Xavier Glorot 初始化是什么
      • 4.2 使用C语言执行 Xavier Glorot 初始化
    • 5. References

1. 目的

用于 lenet 网络训练开始时, weight 和 bias 的初始化。

使用C语言,一方面不想用C标准库的 rand(), 希望沿袭 deepdream_c 的风格; 另一方面, rand() 的跨平台性不太够, RAND_MAX 取值和编译器版本相关,有些编译器下无法得到均等概率的均匀分布。考虑用最少的代码, 实现一个精度相当可以的、性能不算太慢的均匀分布的随机数生成器。

2. 使用 rand() 的正确姿势

假设你的目标平台是唯一的,并且觉得 rand() 的参数 min, max 的范围也是确定的, 使得可以得到比较好的均等概率的均匀分布, 那你可以这样实现:

static float get_random(float min, float max)
{return rand() / ((RAND_MAX + 1U) / (max - min + 1)) + min;
}

3. 使用 TAOCP 公式

代码来自 github, 作者 Bob Adolf. 见参考[2]. ncnn 的单元测试工具, 使用了 prng.h 。这里稍作修改,放在 lenet.c 中:

3.1 实现

//-----------------------------------------------------------------------------------------
// Random Number
//-----------------------------------------------------------------------------------------
// Portable pseudo random number generator by Bob Adolf
// Based on TAOCP, 3.2.2(7), for j=24, k=55, m=2^64
#define LAG1               (UINT16_C(24))
#define LAG2               (UINT16_C(55))
#define RAND_SSIZE         ((UINT16_C(1)) << 6)
#define RAND_SMASK         (RAND_SSIZE - 1)
#define RAND_EXHAUST_LIMIT LAG2
// 10x is a heuristic, it just needs to be large enough to remove correlation
#define RAND_REFILL_COUNT ((LAG2 * 10) - RAND_EXHAUST_LIMIT)
struct prng_rand_t
{uint64_t s[RAND_SSIZE]; // Lagsuint_fast16_t i;        // Location of the current laguint_fast16_t c;        // Exhaustion count
};#define PRNG_RAND_MAX UINT64_MAXstatic inline uint64_t prng_rand(struct prng_rand_t* state)
{uint_fast16_t i;uint_fast16_t r, new_rands = 0;if (!state->c){   // Randomness exhausted, run forward to refillnew_rands += RAND_REFILL_COUNT + 1;state->c = RAND_EXHAUST_LIMIT - 1;}else{new_rands = 1;state->c--;}for (r = 0; r < new_rands; r++){i = state->i;state->s[i & RAND_SMASK] = state->s[(i + RAND_SSIZE - LAG1) & RAND_SMASK]+ state->s[(i + RAND_SSIZE - LAG2) & RAND_SMASK];state->i++;}return state->s[i & RAND_SMASK];
}static inline void prng_srand(uint64_t seed, struct prng_rand_t* state)
{uint_fast16_t i;// Naive seedstate->c = RAND_EXHAUST_LIMIT;state->i = 0;state->s[0] = seed;for (i = 1; i < RAND_SSIZE; i++){// Arbitrary magic, mostly to eliminate the effect of low-value seeds.// Probably could be better, but the run-up obviates any real need to.state->s[i] = i * (UINT64_C(2147483647)) + seed;}// Run forward 10,000 numbersfor (i = 0; i < 10000; i++){prng_rand(state);}
}// Clean up our macros
#undef LAG1
#undef LAG2
#undef RAND_SSIZE
#undef RAND_SMASK
#undef RAND_EXHAUST_LIMIT
#undef RAND_REFILL_COUNT// PRNG_RAND_MAX is exportedstatic struct prng_rand_t g_prng_rand_state;
#define PRNG_SRAND(seed) prng_srand(seed, &g_prng_rand_state)
#define PRNG_RAND()      prng_rand(&g_prng_rand_state)static float get_random(float a, float b)
{//return rand() / ((RAND_MAX + 1U) / (max - min + 1)) + min;float random = ((float)PRNG_RAND()) / (float)(PRNG_RAND_MAX); //RAND_MAX;float diff = b - a;float r = random * diff;return a + r;
}
// End of Random Number
//-----------------------------------------------------------------------------------------

3.2 使用

最简单的用法如下:

void test_random_number()
{PRNG_SRAND(7767517);float val = get_random(0.f, 233);printf("%.6f\n", val);
}

4. 随机数:用于 Xavier Glorot 初始化

4.1 Xavier Glorot 初始化是什么

根据参考[3]知道,如果使用均匀分布初始化,随机数范围是 [ − x , x ] [-x, x] [x,x], 则
x = 6.0 fan in + fan out x = \sqrt{\frac{6.0}{\text{fan}_{\text{in}} + \text{fan}_{\text{out}}}} x=fanin+fanout6.0

如果用于高斯分布 (均值 μ = 0 \mu = 0 μ=0),对应的标准差为
x = 2.0 fan in + fan out x = \sqrt{\frac{2.0}{\text{fan}_{\text{in}} + \text{fan}_{\text{out}}}} x=fanin+fanout2.0

  • fan in \text{fan}_{\text{in}} fanin (float) - 当前网络层的输入神经元个数
  • fan out \text{fan}_{\text{out}} fanout (float) - 当前网络层的输出神经元个数

4.2 使用C语言执行 Xavier Glorot 初始化

这里只考虑均匀分布的情况, 因为本文使用的 prng.h 的代码是生成均匀分布的随机数。

以 lenet-5 的第一层 C1 卷积层为例, 输入为 32x32 单通道图像, 有6个kernel, 每个 kernel 为 5x5 大小。这里仅考虑第一个 kernel, 用 Xavier Glorot 方式初始化它。

输入数量 fan_in = 1, 输出数量 fan_out = 6。kernel 大小 5x5。对每个 kernel 元素, 赋予同样范围的随机数:

    float kernel[5 * 5]; // TODO: initializeint fan_in = 1;int fan_out = 6;float range_abs = m_sqrt(6.0f / (5 * 5 * (fan_in + fan_out)));for (int i = 0; i < 25; i++){kernel[i] = get_random(-range_abs, range_abs);}

使用该 kernel 做卷积, 输入:
在这里插入图片描述

得到的卷积结果(单通道)如下:
在这里插入图片描述

5. References

  1. C/C++随机数用哪个函数?
  2. <github.com/rdadolf/prng/blob/master/prng.h>
  3. https://www.bookstack.cn/read/paddlepaddle-1.6/3f4d0d9266a7a5c8.md

http://www.ppmy.cn/news/449685.html

相关文章

Win11下配置OpenCV with CUDA

前些天在B站上看到用OpenCV调用CUDA加速目标检测和关键点检测的教程&#xff0c;较Pytorch推理速度提升很明显&#xff0c;最近整了个RTX4070&#xff0c;所以就也想来试一试。由于刚换了电脑&#xff0c;没有相关环境&#xff0c;配置过程中踩了一些坑&#xff0c;因此有了这篇…

WIFI测试APP(华为、华三、锐捷)

WIFI测试APP(华为、华三、锐捷) 华为 CloudCampus APP 下载链接&#xff1a;https://serviceturbo-cloud.huawei.com/app/cloudcampus.html 官方介绍链接&#xff1a;https://forum.huawei.com/enterprise/zh/thread-615630.html#pid3280884 华三 Cloudnet APP 下载链接…

华为连接wifi显示wifi未连接服务器,华为手机连上WiFi后显示不可上网?其实很简单,用这个解决就好了...

原标题&#xff1a;华为手机连上WiFi后显示不可上网&#xff1f;其实很简单&#xff0c;用这个解决就好了 一直以来都有很多的小伙伴反应&#xff0c;华为手机经常会出现WiFi连接不上&#xff0c;以及WiFi连接之后不可上网的情况&#xff0c;并且是大家都用同一个网&#xff0c…

华为路由器 wifi网速慢解决办法

华为路由器 wifi网速慢解决办法 华为AR路由器&#xff0c;产品手册里明明写着wifi速率为好几百兆&#xff0c;为啥你的无线网速只有几十兆或者几兆。无线wifi网速慢到底是网络出了问题还是自身配置有问题&#xff1f;别着急&#xff0c;看这里&#xff0c;《如何使WiFi更快(WLA…

华为p20修改WIFI服务器地址,华为P20上网慢怎么办?一招教你开启WLAN+“隐藏”功能提速...

华为P20上网慢怎么办&#xff1f;一招教你开启WLAN“隐藏”功能提速 一、华为P20上网功能介绍 1. 移动网络与WLAN网络之间智能切换 ① WLAN网络覆盖区域如果有可用的网络(有已经成功连接过的WLAN)&#xff0c;手机会根据 WLAN 网络与移动网络信号强度&#xff0c;智能选择最佳的…

华为无线wifi设备连接到服务器,华为中型园区无线网络组网方案及配置教程(上)...

网络架构如下图: 方案目标: 1、通过WLAN部署,提供名为“wlan-net”的无线网络方便用户随时随地接入。 2、接入层可以选用支持PoE功能的S5720LI系列交换机,下挂AP,为用户提供无线网络。 3、汇聚层可以选用S5720HI作为AC设备,在AC上对用户进行集中控制和管理。AC作为DHCP服…

在华为手机上查看连接过的wifi密码(不愁记性不好)

缘起&#xff1a; 当被人问到某处的wifi密码是多少时&#xff0c;自己又曾经连接过但已经忘却了&#xff0c;以下介绍一种方法: 步骤&#xff1a; 1、在桌面上方&#xff08;注意没有图标处&#xff09;下滑 2、点选齿轮图标“设置” 3、点选“无线和网络” 4、点选“WLAN” 5…

华为linux版本wifi驱动,Ubuntu安装无线网卡驱动 Wifi/BT BCM4330 (AP6383)

像BCM4330 (AP6383)这样的Wifi/蓝牙一体化芯片常见于一些平板电脑、笔记本和mini pc中。特别是z3735、z8300、core m这些芯片的平板电脑很常见。AP6210也比较常见,但目前暂未解决其驱动问题。 如何在搭载这样芯片的设备中安装Linux驱动呢?Ubuntu16.04版本的内核中仍然没有封装…