scratch lenet(6): feature_map可视化的C语言实现

news2024/9/22 5:30:19

scratch lenet(6): feature_map可视化的C语言实现

文章目录

  • scratch lenet(6): feature_map可视化的C语言实现
    • 1. 目的
    • 2. FeatureMap 的归一化
      • 2.1 公式
      • 2.2 代码实现
      • 2.3 代码调用
    • 3. 可视化结果
    • 4. References

1. 目的

将卷积层(Convolution)、下采样层(SubSampling,也就是池化)层前向计算结果,归一化后转为图像,保存为文件, 用于可视化感受结果,也用于快速调试排查错误。

卷积、池化的输出结果是 double 类型, 数据范围超过了 [0, 255]. 通过统计最大最小值, 可以执行归一化并转为图像。

实际上还可以用于反向传播更新后的 feature map 的可视化, 不过目前还没实现反向传播, 暂时只有 conv, pool 的前向计算结果 feature map 的可视化。

2. FeatureMap 的归一化

2.1 公式

normalized ( v ) = v − min max − min \text{normalized}(v) = \frac{v - \text{min}}{\text{max} - \text{min}} normalized(v)=maxminvmin

2.2 代码实现

代码实现不依赖于 C 标准库中的 float.h, 因此自行定义 double 类型的最大最小值 M_DBL_MAX, M_DBL_MIN.

为了增加代码复用性、减小复杂度, 每次处理一个通道的 feature map, 输出结果是一张灰度图(传入者负责申请释放内存):

#define M_DBL_MAX ((double)1.79769313486231570814527423731704357e+308L)
#define M_DBL_MIN ((double)2.22507385850720138309023271733240406e-308L)

void get_normalized_gray_image_from_channel(double* channel, int height, int width, uchar* out_image)
{
    double max_value = -M_DBL_MAX;
    double min_value = M_DBL_MAX;
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int idx = i * width + j;
            if (channel[idx] > max_value)
            {
                max_value = channel[idx];
            }
            if (channel[idx] < min_value)
            {
                min_value = channel[idx];
            }
        }
    }

    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int idx = i * width + j;
            out_image[idx] = 255 * (channel[idx] - min_value) / (max_value - min_value);
        }
    }
}

2.3 代码调用

前一小节是实现代码, 还需要知道怎样调用。在卷积、池化层里,对计算结果应用上述函数即可。

保存结果时, 使用了 .pgm 图像格式。 .pgm 图像的读写操作实现代码,见 scratch lenet(1): 读写 pgm 图像文件

void forward_C1()
{ 
    double** kernel = C1_kernel;
    //int in_channel = C1.in_c;
    int kh = C1.kh;
    int kw = C1.kw;
    int out_h = C1.out_h;
    int out_w = C1.out_w;
    int input_h = C1.in_h;
    int input_w = C1.in_w;
    double* input = g_input;
    int number_of_kernel = C1.number_of_kernel;
    double* bias = C1_bias;
    double** output = C1_output;

    for (int k = 0; k < number_of_kernel; k++)
    {
        simple_conv(input, input_h, input_w, kernel[k], kh, kw, output[k], out_h, out_w, bias[k]);
    }

    // 如下是执行 feature map 的归一化、并保存为 .pgm 图像的过程
    const char* save_prefix = "C1_output";
    for (int k = 0; k < number_of_kernel; k++)
    {
        double* channel = output[k];
        
        // get normalized (gray) image from one feature map channel
        uchar* output_image = (uchar*)malloc(sizeof(uchar) * out_h * out_w);
        get_normalized_gray_image_from_channel(channel, out_h, out_w, output_image);

        char savepath[200] = { 0 };
        sprintf(savepath, "%s%d.pgm", save_prefix, k);
        write_pgm_image(output_image, out_w, out_h, savepath);
        free(output_image);
    }
}


void forward_S2()
{
    double** input = C1_output;
    int number_of_kernel = C1.number_of_kernel;
    int kh = S2.kh;
    int kw = S2.kw;
    double** output = S2_output;

    int input_w = S2.in_w;
    int out_h = S2.out_h;
    int out_w = S2.out_w;

    for (int k = 0; k < number_of_kernel; k++)
    {
        double* input_channel = input[k];
        double* output_channel = output[k];
        for (int i = 0; i < out_h; i++)
        {
            for (int j = 0; j < out_w; j++)
            {
                double sum = 0;
                for (int ki = 0; ki < kh; ki++)
                {
                    for (int kj = 0; kj < kw; kj++)
                    {
                        int si = i * 1 + ki;
                        int sj = j * 1 + kj;
                        sum += input_channel[si * input_w + sj];
                    }
                }
                output_channel[i * out_w + j] = sum;
            }
        }

        // 如下是执行 feature map 的归一化、并保存为 .pgm 图像的过程
        const char* save_prefix = "S2_output";
        for (int k = 0; k < number_of_kernel; k++)
        {
            double* channel = output[k];
            
            // get normalized (gray) image from one feature map channel
            uchar* output_image = (uchar*)malloc(sizeof(uchar) * out_h * out_w);
            get_normalized_gray_image_from_channel(channel, out_h, out_w, output_image);

            char savepath[200] = { 0 };
            sprintf(savepath, "%s%d.pgm", save_prefix, k);
            write_pgm_image(output_image, out_w, out_h, savepath);

            free(output_image);
        }
    }
}

3. 可视化结果

第一张图: 左侧为 C1 结果, 正确; 右侧为 S2 结果, 错误, 看起来只对 C1 输出结果的左上 1/4 执行了 conv:(通过可视化,可以为 Debug 快速提供思路):
在这里插入图片描述

第二张图: 修复了 SubSampling 前向计算结果后,得到的 C1 和 S2 的输出结果图:
在这里插入图片描述

4. References

  1. https://en.cppreference.com/w/c/types/limits
  2. scratch lenet(1): 读写 pgm 图像文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/659488.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统之安装Ward服务器监控工具

Linux系统之安装Ward服务器监控工具 一、Ward介绍1.1 Ward简介1.2 Ward特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、部署java环境3.1 jdk下载地址3.2 解压jdk安装包3.3 复制二进制文件3.4 配置环境编辑3.5 查看java版本 四、下载ward的jar包4.1 下载软件包4.2 …

阿里巴巴内部10w+字总结的Java面试题,全是面试官必问技术

献上熬夜整理最新“10w字总结的Java面试题&#xff08;附答案&#xff09;”够你刷&#xff01; 春招升级打怪拿offer&#xff0c;10w字总结的Java面试题&#xff08;附答案&#xff09;够你刷 其包含的内容模块有&#xff1a;基础、JVM、多线程与高并发、Spring、MyBatis、Spr…

APP自动化测试高级定位技巧,我敢打赌你一定不知道

目录 高阶定位-Xpath 包含-contains() XPath 轴 XPath 运算符 AND OR 高阶定位-CSS css selector 定位介绍 css selector 用法 示例 iOS css selector 定位 Toast 识别 Toast 是什么 Toast 定位 Toast 定位 显示等待 使用lambda表达式 总结三种等待方法 高阶定…

Ansible自动化运维工具之playbook剧本编写含lnmp

1.playbook的相关知识 1.1 playbook 的简介 playbook是 一个不同于使用Ansible命令行执行方式的模式&#xff0c;其功能更强大灵活。简单来说&#xff0c;playbook是一个非常简单的配置管理和多主机部署系统&#xff0c;不同于任何已经存在的模式&#xff0c;可作为一个适合部…

【黑马头条】解决P11@EnableDiscoveryClient注解无法导入爆红、bootstrap.yml配置文件图标无法显示成带云朵的小绿叶图标

本期目录 1. 问题描述2. 问题原因3. 解决方法 1. 问题描述 如果按黑马老师给的 heima-leadnews-service 模块的 pom 文件所写的依赖&#xff0c;会发现有 2 个 Bug &#xff1a; 首先&#xff0c;启动类 UserApplication 上的服务发现开关注解 EnableDiscoveryClient 不存在。 …

知识复盘(Session、Mysql、Servlet、Jsp、SSM)

一、会话跟踪技术(Session Tracking&#xff09; 1.为什么会出现会话机制? 目前主流的通讯方式就是客户端和服务端之间进行通信&#xff0c;而这种通信是通过Http协议实现的&#xff0c;但Http协议本身是无状态的&#xff0c;所以客户端每发送一次请求到服务器都会被当做一个…

windows上的Linux系统WSL2

目录 简介 安装 简介 适用于 Linux 的 Windows 子系统 (WSL) 可让开发人员直接在 Windows 上按原样运行 GNU/Linux 环境&#xff08;包括大多数命令行工具、实用工具和应用程序&#xff09;&#xff0c;且不会产生传统虚拟机或双启动设置开销&#xff0c;用于快速的多平台验…

Unity UGUI2——Canvas与EventSystem

一、Canvas ​ Canvas 的意思是画布&#xff0c;它是 UGUI 中所有 UI 元素能够被显示的根本&#xff0c;它主要负责渲染自己的所有 UI 子对象 ​ 如果 UI 控件对象不是 Canvas 的子对象&#xff0c;那么控件将不能被渲染 ​ 我们可以通过修改 Canvas 组件上的参数修改渲染方…

[架构之路-214]- UML-类图图解、详解、结构化、本质化讲解

目录 一、什么是类 1.1 概述 1.2 UML中类的表示 1.3 接口 1.4 抽象类 1.5 模板类 二、什么类图 2.1 概述 2.2 类关系 三、UML类图 3.1 结构关系 3.1.1 完全一体&#xff1a;继承关系 &#xff08;类与类耦合度最高&#xff0c;类与类之间最强的关系&#xff09; …

空天|谈一谈飞机引擎的应急断离

为什么突然想到这个话题呢&#xff1f;归功于即将到来的材料力学考试。在复习科学出版社出版的苟文选、王安强等编写的《材料力学&#xff08;1&#xff09;》第三版第三章的连接键强度校核等内容时&#xff0c;一个例题吸引了我的注意力。 例3-4&#xff1a; 水平力Fg。 强而…

理论实战源码齐飞!架构师社区疯传的SpringSecurity进阶小册真香

安全管理是Java应用开发中无法避免的问题&#xff0c;随着Spring Boot和微服务的流行&#xff0c;Spring Security受到越来越多Java开发者的重视&#xff0c;究其原因,还是沾了微服务的光。作为Spring家族中的一员,其在和Spring家族中的其他产品如SpringBoot、Spring Cloud等进…

Unity UGUI3——三大基础控件

一、Image ​ Image 是图像组件&#xff0c;是 UGUI 中用于显示精灵图片的关键组件 ​ 除了背景图等大图&#xff0c;一般都使用 Image 来显示 UI 中的图片元素 &#xff08;一&#xff09;参数介绍 Source Image&#xff1a;图片来源 图片类型必须是“精灵 Sprite”类型 Col…

IBM N系列存储和NetApp FAS之间的对应关系

IBM在很长一段时间都是OEM NetApp的FAS存储作为他的NAS产品线&#xff0c;在IBM叫做Storage N series&#xff0c;就是N系列&#xff0c;在2014年IBM终止了和NetApp之间的OEM关系&#xff0c;目前在市场上的OEM的NetApp存储型号主要是 FAS3000&#xff0c;FAS31和FAS32的中端系…

HTB靶场:简单inject

HTB靶场&#xff1a;简单inject 1、进入靶场&#xff0c;连接vpn后开启靶机 inject 2、nmap扫描一下靶机 nmap -v -A 10.10.11.*扫描后显示服务和端口信息8080和22 3、打开web服务 1&#xff09;有上传文件功能 简单上传了几个文件&#xff08;txt,img等 上传过程根据参数简…

SpringBoot 实现 PDF 添加水印有哪些方案?

简介 PDF&#xff08;Portable Document Format&#xff0c;便携式文档格式&#xff09;是一种流行的文件格式&#xff0c;它可以在多个操作系统和应用程序中进行查看和打印。在某些情况下&#xff0c;我们需要对 PDF 文件添加水印&#xff0c;以使其更具有辨识度或者保护其版…

全国青少年软件编程(Scratch)等级考试二级考试真题2023年5月——持续更新.....

一、单选题(共25题,共50分) 1.运行下列哪段程序,可以让狗狗走到木屋门口?() A. B. C. D. 标准答案:C 2.下列哪个选项可以控制:按下左键扫帚向左旋转15度,按下右键扫帚向右旋转15度?() A. B. C.

LLM系列 | 09: 基于ChatGPT构建智能客服系统(query分类安全审核防注入)

简介 竹斋眠听雨&#xff0c;梦里长青苔。门寂山相对&#xff0c;身闲鸟不猜。小伙伴们好&#xff0c;我是卖热干面的小女孩。紧接前面几篇ChatGPT Prompt工程系列文章&#xff1a; 04:ChatGPT Prompt编写指南05:如何优化ChatGPT Prompt&#xff1f;06:ChatGPT Prompt实践&am…

如何安装MySQL数据库

目录 什么是MySQL数据库 第一步 安装依赖环境 第二步 创建MySQL相关进程用户 第三步 导入MySQL相关包 第四步 解包到指定目录下 第五步 切换到MySQL目录下编译安装 第六步 编译 第七步 更改指定文件的所有者和所属组 第八步 进入指定配置文件清空内容 第九步 配置指定…

软件测试复习题

一、填空题 软件从“出生”到“消亡”的过程称为___。早期的线性开发模型称为_______开发模型。引入风险分析的开发模型为_______开发模型。ISO 9126-1991标准提出的质量模型包括_______和________和________和______和_______和_______6大特性。按照缺陷的严重程度可以将缺陷…

如何做Web测试?测试者必知的常见测试点总结

目录 一、Web应用程序 二、功能测试 三、易用性测试(界面测试)整体界面测试 四、茶客性测试 五、安全性测试安全性测试要求: 六、性能测试 总结&#xff1a; 如何做Web测试?以下为大家比较全面地总结一下Web测试的票点 一、Web应用程序 应用程序有两种模式&#xff0c…