2024年11月

来源:晓飞的算法工程笔记 公众号,转载请注明出处

论文: CNN Mixture-of-Depths

创新点


  • 提出新的卷积轻量化结构
    MoD
    ,在卷积块(
    Conv-Blocks
    )内通过动态选择特征图中的关键通道进行集中处理,提高效率。
  • CNN MoD
    保留了静态计算图,这提高了训练和推理的时间效率,而且不需要定制的
    CUDA
    内核、额外的损失函数或微调。
  • 通过将
    MoD
    与标准卷积交替使用,能够实现相等性能下的推理加速或相等推理速度下的性能提高。

CNN Mixture-of-Depths


MoD
由三个主要组件组成:

  1. 通道选择器:根据输入特征图与当前预测的相关性选择前
    \(k\)
    个最重要的通道。
  2. 卷积快:从现有架构(如
    ResNets

    ConvNext
    )中进行改编,旨在增强选定通道的特征。
  3. 融合算子:将处理后的通道加到特征图的前
    \(k\)
    个通道上。

通道选择器

通道选择器主要分为两个阶段:

  1. 自适应通道重要性计算:通过自适应平均池化压缩输入特征图,随后通过一个具有瓶颈设计的两层全连接网络进行处理,设定
    \(r = 16\)
    ,最后通过
    sigmoid
    激活函数生成一个分数向量
    \(\mathbf{s} \in \mathbb{R}^C\)
    ,量化了相应通道的重要性。
  2. Top-k
    通道选择与路由:利用重要性分数
    \(\mathbf{s}\)
    选择前
    \(k\)
    个通道输入卷积块处理,原始特征图
    \(X\)
    则直接传递融合算子。

这个选择过程使得通道选择器能够高效地管理计算资源,同时保持固定的计算图,从而实现动态选择要处理的通道。

动态通道处理

每个卷积块中处理的通道数量
\(k\)
由公式
\(k = \lfloor \frac{C}{c} \rfloor\)
决定,其中
\(C\)
表示该块的总输入通道数,
\(c\)
是一个超参数,用于确定通道减少的程度。例如在一个标准的
ResNet
瓶颈块中,通常处理
1024
个通道,设置
\(c = 64\)
会将处理减少到仅
16
个通道(
\(k = 16\)
)。

通过实验发现,超参数
\(c\)
应设置为第一卷积块中输入通道的最大数量,并在整个
CNN
中的每个
MoD
块中保持相同。例如,
ResNet

\(c = 64\)
MobileNetV2

\(c = 16\)

卷积块的最后一步涉及将处理后的通道与从自适应通道重要性计算中获得的重要性评分相乘,确保在训练过程中梯度能够有效地传递回通道选择器,这是优化选择机制所必需的。

融合机制

将处理后的特征添加到
\(X\)
的前
\(k\)
个通道中,保留其余未处理的通道。融合后的特征图
\(\bar{X}\)
具有与原始输入
\(X\)
相同的通道数
\(C\)
,从而保留了后续层所需的维度。

论文在实验中测试了多种将处理后的通道重新集成到特征图
\(X\)
中的策略,包括将处理后的通道添加回其原始位置,但结果并未显示任何改进。实验表明,始终在特征图中使用相同位置来处理信息似乎是有益的,将处理后的通道添加到后
\(k\)
个通道中得到了与添加到前
\(k\)
个通道时相当的结果。

集成到
CNN
结构

MoD
可以集成到各种
CNN
架构中,例如
ResNets

ConvNext

VGG

MobileNetV2,
这些架构被组织成包含多个相同类型(即输出通道数相同)的卷积块(
Conv-Blocks
)的模块。

实验表明,交替使用
MoD
块和标准卷积块在每个模块中是一种最有效的集成方法。需要注意的是,
MoD
块替换每第二个卷积块,从而保持原始架构的深度(例如,
ResNet50
中的
50
层)。每个模块以一个标准块开始,例如
BasicBlock
,然后是一个
MoD
块。

这种交替模式表明,网络能够处理显著的容量减少,只要定期进行全容量卷积。此外,该方法确保
MoD
块不会干扰通常发生在每个模块的第一个块中的空间维度缩减卷积。

主要实验




如果本文对你有帮助,麻烦点个赞或在看呗~
更多内容请关注 微信公众号【晓飞的算法工程笔记】

work-life balance.

给网站免费升级HTTPS协议,可以通过申请并部署免费的SSL证书来实现。以下是一个详细的步骤指南:

一、申请免费SSL证书
选择证书颁发机构:
可以选择像JoySSL这样的公益项目,它提供免费、自动化的SSL/TLS证书颁发服务,适用于各种规模的网站。

免费SSL证书申请入口
提交申请:
登录所选证书颁发机构的官方网站,并创建一个账号,注:在注册的过程中需要填写注册码
230922
来获取免费证书申请权限。
根据需求选择合适的SSL证书类型,如单域名证书、多域名证书或通配符证书。
提交申请,并验证域名的所有权。这通常涉及DNS记录验证、文件验证或邮箱验证等方式。
下载证书:
验证通过后,证书颁发机构会签发证书。
下载收到的SSL证书文件,并解压备用。

二、部署SSL证书
登录服务器:
登录到托管网站的服务器。
上传证书:
将下载的SSL证书文件上传到服务器。
配置Web服务器:
根据所使用的Web服务器(如Apache、Nginx或IIS),修改相应的配置文件。
设置SSL模块和证书路径。
启用HTTPS监听端口(默认为443)。
重定向HTTP请求:
在Web服务器配置中设置规则,将所有HTTP请求自动重定向到对应的HTTPS URL。

三、更新网站链接
检查并更新内部链接:
确保网站上的所有内部链接(包括页面间的链接、CSS、JavaScript、图片等)都使用HTTPS协议。
如果存在混合内容(即页面通过HTTPS加载,但包含HTTP资源引用),浏览器可能会显示警告,影响用户体验和安全性。
使用开发者工具检查:
可以使用浏览器的开发者工具来检查并修正这些问题。

四、其他注意事项
备份网站数据:
在进行任何更改之前,备份网站数据以防万一。
定期更新证书:
免费SSL证书通常有一定的有效期。在证书到期之前,需要重新申请并部署新的证书。
监控和维护:
定期监控SSL证书的有效期和安全性。
使用在线SSL测试工具检查配置是否正确。

通过以上步骤,即可将网站从HTTP免费升级为HTTPS,从而享受加密通信带来的数据安全和用户信任提升。

最近微信出了linux版,用vmware装linux不过瘾,把一台闲置的笔记本装上了Manjaro KDE Plasma,经过一段时间的发展,Linux桌面可用性大大提高。
Kindle->Kindle Mate->Anki这条路在linux下
我用
Kindle ->
KindleVocab
->Anki这么代替了之后,
其他软件都能凑合用,加之用了电信的天翼云电脑后觉得又补全了几乎所有的缺憾

感谢信创,感谢国家,国内大公司出的软件都有信创包了

但是

天翼云电脑只有deb包(
https://desk.ctyun.cn/html/download/)

aur上的也安装不成功

我对yay命令不熟,只有用笨办法解决
首先是下载deb包

装了dpkg
安装失败

看失败信息,把这些包查了下chatgpt后都补上

然后用dpkg的强制安装。虽然报错,但是功能基本正常

前言

最近阅读
Aravis
源码,其中大量运用了GObject,于是打算学习一下。

此系列笔记仅主要面向初学者,不会很深入探讨源码的细节,专注于介绍GObject的基本用法。

此系列笔记参考
GObject Tutorial for beginners

本文可在
个人博客
中阅读,体验更加

套个盾:文中定义的名词只是为了更好地理解GObject,不具备权威性。

类和实例

在GObject中,每个可实例化类类型都与两个结构体相关联:一个是类结构体,一个是实例结构体。

  • 类结构体会被注册到类型系统中(具体注册方式在
    下一节
    讨论),在
    g_object_new
    首次调用时,类型系统会检查相应的类结构体是否已经被初始化为一个类变量,没有则创建并初始化。此后所有该类的实例变量都将共享这个已初始化的类变量。每个类变量只会被创建一次。
  • 每次调用
    g_object_new
    时都会创建实例变量。

在GObject系统中,类结构体和实例结构体都会被实例化,在内存中占有特定的空间。为了便于描述,我们将分配给类结构体的实例称为“类变量”,而分配给实例结构体的实例称为“实例变量”。

GObject实例的结构体定义如下

//file: gobject.h
typedef struct _GObject  GObject;
struct  _GObject
{
  GTypeInstance  g_type_instance;
  
  /*< private >*/
  guint          ref_count;  /* (atomic) */
  GData         *qdata;
};

GObject类的结构体定义如下(我们可以先不用了解结构的细节):

//file: gobject.h
typedef struct _GObjectClass             GObjectClass;
struct  _GObjectClass
{
  GTypeClass   g_type_class;

  /*< private >*/
  GSList      *construct_properties;

  /*< public >*/
  /* seldom overridden */
  GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);
  /* overridable methods */
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
  void       (*dispose)			(GObject        *object);
  void       (*finalize)		(GObject        *object);
  /* seldom overridden */
  void       (*dispatch_properties_changed) (GObject      *object,
					     guint	   n_pspecs,
					     GParamSpec  **pspecs);
  /* signals */
  void	     (*notify)			(GObject	*object,
					 GParamSpec	*pspec);

  /* called when done constructing */
  void	     (*constructed)		(GObject	*object);

  /*< private >*/
  gsize		flags;

  gsize         n_construct_properties;

  gpointer pspecs;
  gsize n_pspecs;

  /* padding */
  gpointer	pdummy[3];
};

下面使用一个简单示例,来演示GObject的类和实例的使用

//file: example01.c
#include <glib-object.h>

int main (int argc, char **argv) 
{

    GObject* instance1,* instance2;     //指向实例的指针
    GObjectClass* class1,* class2;      //指向类的指针
   
    instance1 = g_object_new (G_TYPE_OBJECT, NULL);
    instance2 = g_object_new (G_TYPE_OBJECT, NULL);
    g_print ("The address of instance1 is %p\n", instance1);
    g_print ("The address of instance2 is %p\n", instance2);
 
    class1 = G_OBJECT_GET_CLASS (instance1);
    class2 = G_OBJECT_GET_CLASS (instance2);
    g_print ("The address of the class of instance1 is %p\n", class1);
    g_print ("The address of the class of instance2 is %p\n", class2);
 
    g_object_unref (instance1);
    g_object_unref (instance2);

    return 0;
}

其中:

  • g_object_new
    函数创建实例变量并返回指向它的指针。在实例变量第一次被创建之前,它对应的类变量也会被创建并初始化。
  • 参数
    G_TYPE_OBJECT
    是GObject基类的类型标识符,这是GObject类型系统的核心,所有其他GObject类型都从这个基类型派生。

  • G_OBJECT_GET_CLASS
    返回指向参数所属类变量的指针
  • g_object_unref
    会销毁实例变量并释放内存。

输出:

The address of instance1 is 0x55d3ddc05600
The address of instance2 is 0x55d3ddc05620
The address of the class of instance1 is 0x55d3ddc05370
The address of the class of instance2 is 0x55d3ddc05370

可以发现,两个实例变量的地址不同,但两个实例变量对应的类变量的地址相同,因为两个实例变量共享一个类变量

引用计数

引用计数机制的概念在此不做介绍

在GObject中,GObject实例具有引用计数机制:

//file: example02.c
#include <glib-object.h>
 
static void show_ref_count (GObject* instance) 
{
    if (G_IS_OBJECT (instance))
        /* Users should not use ref_count member in their program. */
        /* This is only for demonstration. */
        g_print ("Reference count is %d.\n", instance->ref_count);
    else
        g_print ("Instance is not GObject.\n");
}
 
int main (int argc, char **argv) 
{
    GObject* instance;

    instance = g_object_new (G_TYPE_OBJECT, NULL);
    g_print ("Call g_object_new.\n");
    show_ref_count (instance);
    g_object_ref (instance);
    g_print ("Call g_object_ref.\n");
    show_ref_count (instance);
    g_object_unref (instance);
    g_print ("Call g_object_unref.\n");
    show_ref_count (instance);
    g_object_unref (instance);
    g_print ("Call g_object_unref.\n");
    g_print ("Now the reference count is zero and the instance is destroyed.\n");
    g_print ("The instance memories are possibly returned to the system.\n");
    g_print ("Therefore, the access to the same address may cause a segmentation error.\n");

    return 0;
}

其中:

  • g_object_new
    创建一个实例变量,然后将变量的引用计数置为1
  • g_object_ref
    将其引用计数加1
  • g_object_unref
    将引用计数减1,如果此时引用计数为0,则析构变量。

输出:

Call g_object_new.
Reference count is 1.
Call g_object_ref.
Reference count is 2.
Call g_object_unref.
Reference count is 1.
Call g_object_unref.
Now the reference count is zero and the instance is destroyed.
The instance memories are possibly returned to the system.
Therefore, the access to the same address may cause a segmentation error.

初始化和析构过程

GObject初始化和销毁的实际过程比较复杂。以下是简单的描述,不做详细说明.

初始化

1.用类型系统注册GObject类型。这是在调用main函数之前的GLib的初始化过程中完成的。(如果编译器是gcc,则
__attribute__ ((constructor))
用于限定初始化函数。)
2.为GObjectClass和GObject结构分配内存
3.初始化GObjectClass结构内存。这个内存将是GObject的类变量。
4.初始化GObject结构内存。这个内存将是GObject的实例变量。

上述初始化过程在第一次调用
g_object_new
函数时执行。在第二次及后续调用
g_object_new
时,它只执行两个过程:①为GObject结构分配内存②初始化内存。

析构

1.销毁GObject实例。释放实例的内存

GObject变量类型是静态类型。静态类型永远不会破坏它的类。因此,即使被销毁的实例变量是最后一个,类变量仍然存在,直到程序终止。

参考文章

1.
GObject Tutorial for beginners

推荐

下一篇:
GObject学习笔记(二)类型创建与注册

简介

PasteForm是贴代码推出的 “新一代CRUD” ,基于ABPvNext,目的是通过对Dto的特性的标注,从而实现管理端的统一UI,借助于配套的PasteBuilder代码生成器,你可以快速的为自己的项目构建后台管理端!目前管理端只有Html+js版本的,后续将支持小程序,Vue等

案例源码

案例源码在

https://gitee.com/pastecode/paste-template

不定期升级

AllInDto!

通过引入PasteForm,一个项目哪怕100个数据表,一般的管理页面也才不到10个,除非有非常多的特殊功能,否则都能用PasteForm中的表格和表单来实现!

image

MarkDown

在开发管理端过程中,有时候也需要使用用到Markdown,之前已经接入了Richtext,本次升级也一并把这个带进去了!
首先你需要从案例项目的PasteTemplate的前端模块
PasteTemplate.HttpApi.Host/wwwroot/page/lib/editor.md/
注意这个里面就是Markdown用到的组件,用的是三方的!

特性信息

如果是字符串,没有设置maxlength,默认就会变更成richtext,也可以手动强制配置,当前特性适用于richtext和markdown

字段 类型 示例 说明
args1 字符 500 配置高度,默认为500
args2 字符 txt 表示另外一个值存储在哪个字段,所以另外一个字段一般设置隐藏
args3 字符 /api/app/Upload/Image 图片上传的地址

以上信息同样适用于richtext

案例UI

image

以上是我用双屏截图的,其实是一个完整的页面,注意看页面的表单中包含了text,textarea,richtext,markdown
那么他对应的dto是如何写的?

Dto对应代码

    /// <summary>
    /// 
    /// </summary>
    public class StringDto
    {
        ///<summary>
        ///姓名 模拟短文本输入
        ///</summary>
        [MaxLength(32)]
        [Required]
        public string Name { get; set; }

        ///<summary>
        ///文本区域 模拟文本区域的输入
        ///</summary>
        [MaxLength(128)]
        public string Desc { get; set; }

        ///<summary>
        ///文本区域 长度大于128则自动为textarea
        ///</summary>
        [MaxLength(256)]
        public string Text { get; set; }

        /////<summary>
        /////文本区域 可以手动指定为textarea,同理你也可以指定为html,text
        /////</summary>
        //[MaxLength(128)]
        //[ColumnDataType("textarea")]
        //public string Mark { get; set; }

        ///<summary>
        ///富文本 模拟富文本,前端HTML的是使用wangEditv5,默认不配置maxlength的就是html
        ///</summary>
        public string Blog { get; set; }

        /////<summary>
        /////MarkDown1 
        /////</summary>
        //[ColumnDataType("markdown")]
        //public string Mark1 { get; set; }

        ///<summary>
        ///MarkDown2 有默认值的
        ///</summary>
        [ColumnDataType("markdown")]
        public string Mark2 { get; set; } = "## 今日成果";

    }

由上面代码可知,只要在对应的字段上配置
[ColumnDataType("markdown")]即可
或者你也可以配置特性为[PasteMarkDown]
他们两个是等效的,可以说ColumnDataTypeAttribute是所有贴代码框架特性的基础属性
像PasteClass,PasteHidden,PasteButton等最终都是为了转化成ColumnDataTypeAttribute

提交信息

上面的UI中,我们是随便填写点东西后,提交,看到的提交信息如下

image

暂时先忽略mark2mdeditor-html-code和mark2mdeditor-markdown-doc字段,这个是markdown框架里面带了name被parseform来了,后续再考虑去掉他!
从上面的提交可以看到mark2是有内容的!

符合预期!

注意

由于markdown和richtext的特殊性,关于字段长度的设置需要按照实际来填写!
看特性信息args2,这个字段如何配置了,则需要对这个字段的特性标注为hidden
这样在UI上args2的字段是不显示的,而提交数据给后台,则可以接收到!

我们下期将介绍select和reload的巧妙结合案例... .. .