scratch lenet(5): 快速生成随机数的C语言实现 您所在的位置:网站首页 c语言随机生成随机数 scratch lenet(5): 快速生成随机数的C语言实现

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

2023-06-21 06:51| 来源: 网络整理| 查看: 265

文章目录 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)) uint_fast16_t i; uint_fast16_t r, new_rands = 0; if (!state->c) { // Randomness exhausted, run forward to refill new_rands += RAND_REFILL_COUNT + 1; state->c = RAND_EXHAUST_LIMIT - 1; } else { new_rands = 1; state->c--; } for (r = 0; r uint_fast16_t i; // Naive seed state->c = RAND_EXHAUST_LIMIT; state->i = 0; state->s[0] = seed; for (i = 1; 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 exported static 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​+fanout​6.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​+fanout​2.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: initialize int 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


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有