【Darknet】模型隐藏的一种方法

2023-11-18

之前在一个公司实习的时候有个需求,说要把Darknet的模型隐藏起来。就是说提供给用户的只有dll,而cfg和weights文件不能直接给客户,不然就暴露商业机密了嘛。所以就研究了一下如何隐藏模型,主要参考这篇文章。具体原理咱也不是很懂,反正只要能隐藏就行了。下面介绍一下Darknet隐藏模型的方法。
 
 

一、准备

首先准备好VS,然后配置opencvcuda/cudnn,这两个好像不是必须的,但本文都是配好的。在Darknet官网下载最新版的源码。
 

二、封装模型

打开工程yolo_cpp_dll.sln(无显卡为yolo_cpp_dll_no_gpu.sln),准备好要封装的模型(cfg)和权重(weights)。右键工程->添加->资源->导入,选择模型文件(这里用yolov4-tiny -> 详情)并命名为cfg(随意),同理把权重也添加上去。

在这里插入图片描述
这时系统会自动生成resource.h(在build/darknet下)

在这里插入图片描述说明资源添加成功。
 

三、调用资源

Darknet中供外部调用的接口是yolo_v2_class.hpp,先把名字改成xxx.h(随意,但总不能暴露你用的yolo吧,高大上一点),对应的.c也改了,然后重新添加到工程。在xxx.c中添加调用资源代码

//load cfg
char* LoadCfg(int &len)
{
    HMODULE phexmodule = LoadLibrary("xxx.dll");
    HRSRC hRsrc = FindResource(phexmodule, MAKEINTRESOURCE(101), TEXT("cfg"));
    DWORD dwSize = SizeofResource(phexmodule, hRsrc);
    len = dwSize;
    HGLOBAL hGlobal = LoadResource(phexmodule, hRsrc);
    LPVOID pBuffer = LockResource(hGlobal);
    char *s = (char *)pBuffer;
    return s;
}
//load weights
float* LoadWeights(int &len)
{
    HMODULE phexmodule = LoadLibrary("xxx.dll");
    HRSRC hRsrc = FindResource(phexmodule, MAKEINTRESOURCE(102), TEXT("weights"));
    DWORD dwSize = SizeofResource(phexmodule, hRsrc);
    len = dwSize;
    HGLOBAL hGlobal = LoadResource(phexmodule, hRsrc);
    LPVOID pBuffer = LockResource(hGlobal);
    float *s = (float *)pBuffer;
    return s;
}

注意LoadLibrary里的参数应与生成的dll文件名相同(默认则是工程名),FindResource中的资源编号与resource.h中生成的相同,名字与第二步中你给这个资源的命名相同。

把Detector类的构造函数前两个参数删掉,只保留gpuid这个参数。因为这两个参数要从内存中(也就是dll中)读,就不需要给客户留接口了。在Detector类的构造函数中将读模型和权重的代码替换为

int cfg_len;
char *cfg = LoadCfg(cfg_len);
int weights_len;
float *weights = LoadWeights(weights_len);
net = parse_network_cfg_custom_from_memory(cfg, cfg_len);
load_weights_from_memory(&net, weights, weights_len);

并删除其他依赖文件的相关代码。
 

四、改写代码

源代码中的cfg和weights文件分别是从函数parse_network_cfg_custom和load_weights_upto读进来的,下面来实现从内存中读取。

(1)模型

在parser.h中添加

network parse_network_cfg_custom_from_memory(char *cfg, int cfg_len);

复制一份parser.c中的parse_network_cfg_custom的实现并把函数改为上述形式,然后将第一行改为

list *sections = read_cfg_from_memory(cfg, cfg_len);

read_cfg_from_memory的实现如下

list *read_cfg_from_memory(char *cfg, int cfg_len)
{
    int i, j, linenums = 0;
    list *sections = make_list();
    section *current = 0;
    for (i = 0; i < cfg_len; i++)
    {
        char *line = (char *)malloc(256 * sizeof(char));
        for (j = 0; cfg[i] != '\n'; i++, j++)
        {
            line[j] = cfg[i];
        }
        line[j] = '\0';
        ++linenums;//行数
        strip(line);//去掉特殊字符
        switch (line[0])
        {
            case '[':
                current = (section*)xmalloc(sizeof(section));
                list_insert(sections, current);
                current->options = make_list();
                current->type = line;
                break;
            case '\0':
            case '#':
            case ';':
            case ' ':
            case '\t':
                free(line);
                break;
            default:
                if (!read_option(line, current->options)) {
                    fprintf(stderr, "Config file error line %d, could parse: %s\n", linenums, line);
                    free(line);
                }
                break;
        }
    }
    return sections;
}

其实现原理基本和函数read_cfg相同,区别是从以*cfg为头指针,长度为cfg_len的内存区域读取模型。

(2)权重

权重同理,在parser.h中添加

void load_weights_from_memory(network *net, float *weights, int weights_len);

其实现为

void load_weights_from_memory(network *net, float *weights, int weights_len)
{
#ifdef GPU
    if(net->gpu_index >= 0){
        cuda_set_device(net->gpu_index);
    }
#endif
    fprintf(stderr, "Loading weights from memory");
    fflush(stdout);

    int i, start = 5;
    int *index = &start;
    for(i = 0; i < net->n ; ++i)
    {
        layer l = net->layers[i];
        if (l.dontload) continue;
        if(l.type == CONVOLUTIONAL && l.share_layer == NULL){
            load_convolutional_weights_from_memory(l, weights, index);
        }
        if (l.type == SHORTCUT && l.nweights > 0) {
            load_shortcut_weights_from_memory(l, weights, index);
        }
        if(l.type == BATCHNORM){
            load_batchnorm_weights_from_memory(l, weights, index);
        }
		……

这个函数很长,后面还有没放出来的。但因为我封装的是yolov4-tiny,用不到connect、rcnn那些层,所以后面全删了。这里要看你封装的是什么模型,需要什么层,再去写那些层的load_xxx_weights_from_memory。以卷积层为例,其load函数的实现为

void load_convolutional_weights_from_memory(layer l, float *weights, int *index)
{
    int num = l.nweights;
    int read_bytes;
    myLoad(l.biases, weights, l.n, index);
    if (l.batch_normalize && (!l.dontloadscales))
    {
        myLoad(l.scales, weights, l.n, index);
        myLoad(l.rolling_mean, weights, l.n, index);
        myLoad(l.rolling_variance, weights, l.n, index);
        if (0)
        {
            int i;
            for (i = 0; i < l.n; ++i) {
                printf("%g, ", l.rolling_mean[i]);
            }
            printf("\n");
            for (i = 0; i < l.n; ++i) {
                printf("%g, ", l.rolling_variance[i]);
            }
            printf("\n");
        }
        if (0)
        {
            fill_cpu(l.n, 0, l.rolling_mean, 1);
            fill_cpu(l.n, 0, l.rolling_variance, 1);
        }
    }

    myLoad(l.weights, weights, num, index);

    if (l.flipped) {
        transpose_matrix(l.weights, (l.c / l.groups)*l.size*l.size, l.n);
    }
#ifdef GPU
    if (gpu_index >= 0) {
        push_convolutional_layer(l);
    }
#endif
}

*weights为权重文件指针,index为索引,从5开始是因为前5个int字符都有别的含义,不是权重本身(详见load_weights_upto函数)。而myLoad函数就是读内存了

void myLoad(float *destination, float *source, int num, int *index)
{
    for (int i = 0; i < num; i++)
    {
        destination[i] = source[*index];
        (*index)++;
    }
}

其他load_xxx_weights_from_memory函数也同理,只要从load_xxx_weights拷贝一份代码,并把所有的

fread(xxx, sizeof(float), length, fp);

变成

myLoad(xxx, weights, length, index);

即可。至此核心代码完成,生成dll。如果有其他报错(我被window.h坑过==)百度搜索即可。
 

五、测试

新建工程,添加刚才的xxx.h、resource.h及lib、dll文件,创建main.cpp,添加测试代码

#include "xxx.hpp"
#include "resource.h"
#include<iostream>
#include<cstring>
#include<string.h>
#include<stdlib.h>
using namespace std;
int main()
{
	string img = "test.jpg";
	Detector detector;
	vector<bbox_t> result_vec = detector.detect(img);
	for (auto &i : result_vec)
	{
		cout << "obj_id = " << i.obj_id << ",  x = " << i.x << ", y = " << i.y << ", w = " << i.w
			<< ", h = " << i.h << ", prob = " << i.prob << endl;
	}
	return 0;
}

能成功运行并输出检测结果(类别id和坐标,具体id对应哪一类需要查看names文件)就表示封装成功了。如果没结果,也不代表封装失败,可能是没检测到东西,降低阈值再试试。输出结果中默认打印了很多网络信息,如果不想输出这些信息,可以在parse_convolutional和make_convolutional_layer(以卷积层打印的信息为例,其它层同理)中注释掉相关打印代码即可。当然,如果你想加入一些其他的输出信息也可自行添加。最终我的输出结果为
在这里插入图片描述
代码不传git了,有需要私聊。如果哪位大佬有更好的隐藏模型的方法也欢迎赐教。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Darknet】模型隐藏的一种方法 的相关文章

随机推荐

  • 【论文精读】QLORA: Efficient Finetuning of Quantized LLMs

    QLORA Efficient Finetuning of Quantized LLMs 前言 Abstract Introduction Background Block wise k bit Quantization Low rank
  • element的嵌套dialog,在打开第二个dialog的时候会被遮罩层遮住

    element嵌套dialog遮罩层的问题 做项目的时候遇到一个问题 在一个弹出层的内容区做处理打开另一个弹出层时 第二个弹出层会被遮罩层遮住 点击五次之后遮罩层关闭 然后才发现 element 的dialog嵌套问题 之所以第二次弹出的会
  • 华为OD机试真题 Java 实现【区块链文件转储系统】【2023Q2 200分】,附详细解题思路

    一 题目描述 区块链底层存储是一个链式文件系统 由顺序的N个文件组成 每个文件的大小不一 依次为F1 F2 Fn 随着时间的推移 所占存储会越来越大 云平台考虑将区块链按文件转储到廉价的SATA盘 只有连续的区块链文件才能转储到SATA盘上
  • 安全基础 --- JSON + 函数声明

    JSON 格式 JSON JavaScript Object Notation缩写 是一种用于数据交换的文本格式 目的是取代繁琐笨重的 XML 格式 1 规定 复合类型的值只能是数组或对象 不能是函数 正则表达式对象 日期对象 原始类型的值
  • centos是arm还是amd_amd系列cpu安装linux

    如何在 Linux 上安装 Intel 微代码固件 linux 的内核可以在引导时更新 cpu 固件 而无需 bios 更新 处理器的微码保存在内存中 在每次启动系统时 内核可以更新这个微码 这些来自 intelamd 的微码的更新可以去修
  • unity多个贴图shader

    在一个Material上放多个贴图的测试 比如一个plane上放四个贴图 Shader aaa Properties MainTex Texture 2D white MainTex2 Texture 2D white MainTex3 T
  • Summer Holiday HDU - 1827 强连通分量+缩点

    To see a World in a Grain of Sand And a Heaven in a Wild Flower Hold Infinity in the palm of your hand And Eternity in a
  • http 11ajax.co,Internet Explorer 11 crashes when Angulars $http.post is used with large/complex json...

    I am consistently able to crash IE11 when I post large complex json object using Angulars http post method I have setup
  • (最详细!)汇编原理中OF(溢出)和CF(进位)的判断方法

    关于这个问题百度查了半天 都没有比较全面的解释 所以我来了 首先我们需要知道 al是用两位16进制的数来保存数据的 所以正数最多保存0 255 十进制 FFH 处理器内部以补码表示有符号数 8个二进制位能够表达的整数范围是 127 128
  • llama2模型部署方案的简单调研-GPU显存占用(2023年7月25日版)

    先说结论 全精度llama2 7B最低显存要求 28GB 全精度llama2 13B最低显存要求 52GB 全精度llama2 70B最低显存要求 280GB 16精度llama2 7B预测最低显存要求 14GB 16精度llama2 13
  • three.js 楼层加载动画

    three js 楼层加载动画 概述 创建场景 创建天空 创建水面 创建模型 开启模型动画 栅栏动画 概述 如有不明白的可以加QQ 2354528292 wx aichitudousien 更多教学视频请访问 https space bil
  • 跋20230325

    很久没有登录csdn 突然上号一次发现n多的评论和私信 时间跨度从2021年到2023年 感谢大家的信任 但是消息太多 且现在笔者已经没有从事相关行业 大家的问题很有深度 都是我答不上来的 这里就不一 一进行回复了 请大佬们见谅 博文都是笔
  • java字符串基础操作

    1 字符串的特点 java lang String使用了final修饰 不能被继承 字符串底层封装了字符数组及针对字符数组的操作算法 字符串一旦创建 对象内容永远无法改变 但字符串引用 可以重新赋值 Java字符串在内存中采用Unicode
  • 今天发现rabbitMQ消息堆积

    发现有三十多万的消息堆积在10的queue里没有被消费 记录一下查看问题的步骤 1 jps 找出程序的PID 2 jstack PID 查看线程dump 发现rabbitMQ的consumer worker线程block住了 Thread
  • 转眼就来字节六个月了,真的不一样

    今天来分享一个学弟 也是我老乡 阿秀 他入职字节跳动 6 个月的感受 希望给向往大厂的小伙伴一点点信心和动力 一 过去 首先来回复下有些小伙伴对字节存在的疑问 1 字节 996 的生活长期来看没有啥意义 我觉得还是挺有意义的 因为见识到了自
  • 前后端分离总结(部分)

    本文内容转载于博客 前后端分离架构概述 https blog csdn net fuzhongmin05 article details 81591072 1 背景 前后端分离已成为互联网项目开发的业界标准使用方式 通过nginx tomc
  • Springboot中使用netty 实现 WebSocket 服务

    依赖
  • C语言:写文件

    C语言 写文件 在C语言中 我们可以使用标准库函数来创建 打开 写入和关闭文件 文件操作是处理数据的重要方面之一 它允许我们将数据永久保存在磁盘上 并在需要时进行读取和修改 本文将介绍如何在C语言中写入文件 并提供相应的源代码示例 首先 我
  • [Dynamics CRM]错误代码参考

    转载于 http it zhaozhao info archives 20266 0x80048472 One or more imports are not in completed state Imported records can
  • 【Darknet】模型隐藏的一种方法

    之前在一个公司实习的时候有个需求 说要把Darknet的模型隐藏起来 就是说提供给用户的只有dll 而cfg和weights文件不能直接给客户 不然就暴露商业机密了嘛 所以就研究了一下如何隐藏模型 主要参考这篇文章 具体原理咱也不是很懂 反