首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,711 阅读
2
主板开机跳线接线图【F_PANEL接线图】
11,204 阅读
3
移动光猫获取超级密码&开启公网ipv6
9,447 阅读
4
Linux使用V2Ray 原生客户端
7,771 阅读
5
NVIDIA 显卡限制功率
3,611 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
k8s
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
360
篇文章
累计收到
119
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
k8s
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
360
篇与
的结果
2022-01-13
MySQL学习:SQL关联查询的七种JOIN
1.图示2.案例2.0 准备数据以一个简易问答系统为例,包括问题表和问题所属标签,问题表如下:CREATE TABLE `t_qa` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `title` varchar(200) NOT NULL DEFAULT '' COMMENT '标题', `answer_count` int(5) unsigned NOT NULL DEFAULT '0' COMMENT '回答个数', `label_id` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '标签id', `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', `create_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间', `update_by` bigint(20) unsigned DEFAULT NULL COMMENT '更新人', `update_date` datetime DEFAULT NULL COMMENT '更新时间', `del_flag` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0:不删除,1:删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `t_qa` (`id`, `title`, `answer_count`, `label_id`, `create_by`, `create_date`, `update_by`, `update_date`, `del_flag`) VALUES (1, 'Java是什么?', 5, 1, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (2, 'PHP是什么?', 4, 2, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (3, '前端是什么?', 3, 3, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (4, 'nodejs是什么?', 2, 0, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (5, 'css是什么?', 1, 0, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (6, 'JavaScript是什么?', 0, 0, 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0);标签表如下:CREATE TABLE `t_label` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称', `create_by` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '创建人', `create_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间', `update_by` bigint(20) unsigned DEFAULT NULL COMMENT '更新人', `update_date` datetime DEFAULT NULL COMMENT '更新时间', `del_flag` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0:不删除,1:删除', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `t_label` (`id`, `name`, `create_by`, `create_date`, `update_by`, `update_date`, `del_flag`) VALUES (1, 'java', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (2, 'php', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (3, '大前端', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (4, 'mybatis', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (5, 'python', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0), (6, '多线程', 0, '2017-08-24 17:43:53', 0, '2017-08-24 17:43:53', 0);2.1 左连接(LEFT JOIN)问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?1NULLNULLSELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id2.2 右连接(RIGHT JOIN)问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端NULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id2.3 内连接(INNER JOIN)问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq INNER JOIN t_label tl ON tq.label_id = tl.id2.4 左独有连接(LEFT JOIN)问题回答个数标签id标签名称nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?0NULLNULLSELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id WHERE tl.id IS NULL2.5 右独有连接(RIGHT JOIN)问题回答个数标签id标签名称NULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id WHERE tq.label_id IS NULL2.6 全连接(FULL JOIN)由于MySQL不支持FULL OUTER JOIN,所以如果有全连接需求时,可用表达式:full outer join = left outer join UNION right outer join来实现。问题回答个数标签id标签名称Java是什么?51javaPHP是什么?42php前端是什么?33大前端nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?0NULLNULLNULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id UNION SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id 2.7 全连接去交集(FULL JOIN)问题回答个数标签id标签名称nodejs是什么?2NULLNULLcss是什么?1NULLNULLJavaScript是什么?0NULLNULLNULLNULL4mybatisNULLNULL5pythonNULLNULL6多线程SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq LEFT JOIN t_label tl ON tq.label_id = tl.id WHERE tl.id IS NULL UNION SELECT tq.title, tq.answer_count, tl.id, tl.name FROM t_qa tq RIGHT JOIN t_label tl ON tq.label_id = tl.id WHERE tq.label_id IS NULL参考资料一张图看懂 SQL 的各种 JOIN 用法SQL七种JOIN解析【MySQL笔记】七种JOIN的SQL
2022年01月13日
873 阅读
0 评论
0 点赞
2022-01-13
tqdm:简单好用的python进度条
1.介绍Tqdm是一个快速,可扩展的Python进度条,可以在 Python 长循环中添加一个进度提示信息,用户只需要封装任意的迭代器 tqdm(iterator)。安装方式如下:pip install tqdm2.简单使用2.1 用法一from tqdm import tqdm import time for i in tqdm(range(1000)): time.sleep(0.01) #do something100%|██████████| 1000/1000 [00:10<00:00, 95.17it/s]2.2 用法二from tqdm import trange import time for i in trange(100): time.sleep(0.01) #do something100%|██████████| 100/100 [00:01<00:00, 94.86it/s]2.3 用法三from tqdm import tqdm import timepython pbar = tqdm(total=100) for i in range(100): time.sleep(0.05) #do something pbar.update(1) pbar.close()100%|██████████| 100/100 [00:05<00:00, 19.35it/s2.4 用法四(具有前后缀描述功能)(推荐)from tqdm import tqdm import time import random pbar = tqdm(total=100) for i in range(100): time.sleep(0.05) #do something pbar.update(1) pbar.set_description("Epoch: %d" % 1) # 设置前缀 pbar.set_postfix({'loss':random.random()}) # 设置后缀 pbar.close()Epoch: 1: 100%|██████████| 100/100 [00:07<00:00, 14.28it/s, loss=0.709] 参考资料tqdm介绍及常用方法在tqdm中加入前缀和后缀详细介绍Python进度条tqdm的使用
2022年01月13日
686 阅读
0 评论
0 点赞
2022-01-11
Leanote(蚂蚁笔记):开源个人云笔记搭建
1.介绍Leanote是一款开源云笔记软件,具备 markdown 输入,代码高亮,多人协作,笔记历史记录,笔记内导航,直接发布为博客等等能力。特性高效笔记:Leanote 有易操作的界面, 包含一款富文本编辑器和Markdown编辑器,让您的笔记记录更轻松和高效。对高阶用户,我们还提供Vim 和Emacs 编辑模式,助推你的写作速度更上层楼。知识管理: Leanote 灵活而强大的“笔记本-笔记-标签”系统,让它成为你个人知识管理的利器。分享: 你可以通过Leanote同好友分享知识、想法和经历, 邀请好友加入你的笔记簿,通过云端交流信息。协作: Leanote协助你与同事之间相互协作,激荡新思路,随时随地头脑风暴。博客: Leanote也可以作为你的个人博客, 把你的知识传播的更远!其它特性支持Markdown编辑写作模式Vim 及 Emacs 编辑模式支持PDF导出支持批量操作博客自定义主题, 实现高度定制化2.搭建教程2.1 安装 MongodbcenterOS1、进入到yum仓库中,配置MongoDB的yum源。vim /etc/yum.repos.d/mongodb-org.repo2、通过点击 i 进入编辑模式,并在mongodb-org.repo中添加如下代码。[mongodb-org] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.6/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc注意:name #名称baseurl #获得下载的路径gpkcheck=1 #表示对从这个源下载的rpm包进行校验;enable=1 #表示启用这个源。gpgkey #gpg验证3、配置完yum源之后,通过ESC键退出并输入:wq保存退出,然后重新加载下yum源。yum list4、下载Mongodb。yum install mongodb-org -y5、配置MongoDB配置文件。vim /etc/mongod.conf 通过点击 i 进入编辑模式,修改其中的bindIp,配置完后,通过ESC键退出并输入:wq保存退出。net: port: 27017 bindIp: 0.0.0.0 6、执行以下命令,启动MongoDB服务。systemctl start mongod.service7、执行以下命令,查看MongoDB运行状态。systemctl status mongodubuntu1、首先在Mongodb官网下载最新版本。MongoDB 源码下载地址:https://www.mongodb.com/download-center#communitywget https://fastdl.mongodb.org/linux/mongodb-shell-linux-x86_64-ubuntu1804-5.0.5.tgz2、接着解压文件tar -xzvf mongodb-linux-x86_64-3.0.1.tgz/3、修改/etc/profile文件,配置环境变量,添加下面的命令在/etc/profile文件的最后面即可。export PATH=$PATH:/home/userone/mongodb-linux-x86_64-3.0.1/bin4、执行下面的命令使更改生效source /etc/profile5、新建配置文件mongod.confsystemLog: destination: file path: "/home/no4/mongodata/log/mongod.log" logAppend: true storage: dbPath: "/home/no4/mongodata/db" journal: enabled: true processManagement: fork: true net: bindIp: localhost port: 270175、在配置文件所指定的位置建立对应的文件夹mkdir /home/userone/data6、mongod -f /home/no4/mongodb/mongod.conf命令启动mongodb服务器,正常输出如下:root@no4-Default-string:/home/no4# mongod -f ./mongodb/mongod.conf about to fork child process, waiting until server is ready for connections. forked process: 11330 child process started successfully, parent exiting输入下面的命令启动它mongo如果没有提示错误,这说明Mongodb安装成功。2.2 安装Leanote1. 下载Leanote二进制安装包。wget --no-check-certificate https://nchc.dl.sourceforge.net/project/leanote-bin/2.6.1/leanote-linux-amd64-v2.6.1.bin.tar.gz2. 解压安装包。tar -zxvf leanote-linux-amd64-v2.6.1.bin.tar.gz3. 编辑文件leanote/conf/app.conf,在文件中找到app.secret项,将该项的值改为任意字符串。(如不修改将会有安全风险)。说明:根据Leanote官方文档,如不修改app.secret项的值,将会有安全隐患。a. 使用vim编辑器打开文件leanote/conf/app.conf。vim leanote/conf/app.confb. 进入vim编辑器后,输入:/app.secret=并按下回车查找app.secret位置。c. 找到该项位置后按下i键进入编辑模式,修改该项的值为任意字符串。d. 修改完成后,按下esc键退出编辑模式,输入:wq保存并退出vim编辑器。修改后如图所示。4. 初始化数据库。mongorestore -h localhost -d leanote --dir /root/leanote/mongodb_backup/leanote_install_data/5. 启动服务。nohup bash /root/leanote/bin/run.sh > /root/leanote/run.log 2>&1 &6. 访问云笔记。在浏览器中访问http://<IP>:9000,管理用户为admin,密码为abc123。登录成功后如下图所示。参考资料https://developer.aliyun.com/adc/scenario/b1ac9deecdb143469de985471b440aa7?spm=a2c6h.13858375.0.0.511279a9x0qQG8https://github.com/leanote/leanote云服务器搭建开源个人云笔记Leanote(蚂蚁笔记)Ubuntu下mongodb的安装与使用
2022年01月11日
1,950 阅读
0 评论
0 点赞
2022-01-11
MySQL学习:MySQL常用命令
1.连接MySQL1.1 连接到本机上的MySQLmysql -u root -p # 如果刚安装好MySQL,root是没有密码的1.2 连接到远程主机上的MySQL假设远程主机的IP为:192.168.206.100,用户名为root,密码为12345678。mysql -h 192.168.206.100 -u root -p 12345678; 1.3 退出MySQLexit; # or quit;2. 修改root密码与简单权限管理2.1 修改root密码方法1: 用SET PASSWORD命令mysql -u root mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('newpass');方法2:用mysqladminmysqladmin -u root password "newpass" # 如果root已经设置过密码,采用如下方法 mysqladmin -u root password oldpass "newpass"方法3: 用UPDATE直接编辑user表mysql -u root mysql> use mysql; mysql> UPDATE user SET Password = PASSWORD('newpass') WHERE user = 'root'; mysql> FLUSH PRIVILEGES;方法4:在丢失root密码的时候mysqld_safe --skip-grant-tables& mysql -u root mysql mysql> UPDATE user SET password=PASSWORD("new password") WHERE user='root'; mysql> FLUSH PRIVILEGES;2.2 允许用户远程登录允许root使用密码admin123从任何主机连接到mysql服务器GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'admin123' WITH GRANT OPTION; -- 其中"*.*"代表所有资源所有权限, “'root'@%”其中root代表账户名,%代表所有的访问地址,也可以使用一个唯一的地址进行替换,只有一个地址能够访问。如果是某个网段的可以使用地址与%结合的方式,如10.0.42.%。IDENTIFIED BY 'root',这个root是指访问密码。WITH GRANT OPTION允许级联授权。 flush privileges; -- 刷新访问权限表允许用户root使用密码admin123从ip为192.168.12.16的主机连接到mysql服务器GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.1.16' IDENTIFIED BY 'admin123' WITH GRANT OPTION; flush privileges;3.库操作create database db_name; -- 创建数据库 show databases; -- 显示所有的数据库 drop database db_name; -- 删除数据库 use db_name; -- 使用数据库4.表操作建表与删表create table tb_name (字段名 varchar(20), 字段名 char(1)); -- 建表 show tables; -- 显示数据表 desc tb_name; -- 显示表结构 drop table tb_name; -- 删除表eg:-- 创建学生表 create table Student( Sno char(10) primary key, Sname char(20) unique, Ssex char(2), Sage smallint, Sdept char(20) );修改表结构-- 增加字段 alter table table_name add field_name field_type; -- 删除字段 alter table table_name drop field_name; -- 修改字段的注释 alter table `student` modify column `id` comment '学号'; -- 修改原字段名称及类型 alter table table_name change old_field_name new_field_name field_type; -- 加索引 alter table 表名 add index 索引名 (字段名1[,字段名2 …]); -- 删除某个索引 alter table employee drop index emp_name; -- 设置主关键字 alter table 表名 add primary key (字段名); -- 设置唯一限制条件 alter table 表名 add unique 索引名 (字段名); 5.数据操作5.1 插入数据(Add)-- 第一种形式无需指定要插入数据的列名,只需提供被插入的值即可: insert into tb_name values (value1,value2,value3,...); -- 第二种形式需要指定列名及被插入的值: insert into tb_name (column1,column2,column3,...) values (value1,value2,value3,...);eg:insert into Student values ( 20180001,张三,男,20,CS); insert into Student values ( 20180002,李四,男,19,CS); insert into Student (Sno,Sname,Ssex,Sage,Sdept) values ( 20180003,王五,男,18,MA); insert into Student (Sno,Sname,Ssex,Sage,Sdept) values ( 20180004,赵六,男,20,IS);5.2 查询数据(Select)select 语句的一般格式如下:select <目标列表达式列表> [into 新表名] from 表名或视图名 [where <条件>] [group by <分组表达式>] [having <条件>] [order by <排序表达式>[ASC|DESC]]查询所有-- 查询表中所有列 select * from tb_name; -- 查询表中指定的列 select tb_name.<字符型字段>,<字符型字段> ... from tb_name; -- 指定查询结果中的列名 select <字符型字段> as 列标题1,<字符型字段> as 列标题2, <字符型字段> as 列标题3 from bt_name; -- 查询经过计算的列(即表达式的值) select <字符型字段>,<字符型字段>,列标题 = <字符型字段> * n from tb_name;消除查询结果中的重复行select distinct <字符型字段>[,<字符型字段>,...] from tb_name;限制查询结果中的返回行数select top n from tb_name; -- 查询前 n 的数据 select top n percent from tb_name; -- 查询前 n% tb_name的数据对查询结果排序select * from tb_name order by <排序表达式> <排序方法>;查询满足条件的行模板select [all|distinct] [top n[percent]]<目标列表达式列表> from 表名 where <条件>; -- 说明:在查询条件中可使用以下运算符或表达式: -- 比较运算符 <=,<,=,>,>=,!=,<>,!>,!< -- 范围运算符 between... and,not between... and -- 列举运算符 in,not in -- 模糊匹配运算符 like,not like -- 空值运算符 is null,is not null -- 逻辑运算符 and,or,not具体应用-- 使用比较运算符 select * from tb_name where <字段> >= n ; -- 指定范围 select * from tb_name where <字段> [not] between <表达式1> and <表达式2>; -- 使用列举 select * from tb_name where <字段> [not] in(值1,值2,...,值n); -- 使用通配符进行模糊查询 匹配串中通常含有通配符%和_(下划线)。 select * from tb_name where <字符型字段> [not] like <匹配串>; -- 使用null的查询 select * from tb_name where <字符型字段> is [not] null; -- 多重条件查询:使用逻辑运算符 select * from tb_name where <字符型字段> = 'volues' and <字符型字段> > n;使用统计函数-- 求指定的数值型表达式的和或平均值 select avg(<字符型字段>) as 平均数,sum(<字符型字段>) as 总数 from tb_name where <字符型字段> ='字符串'; -- 求指定表达式的最大值或最小值。 select max(<字符型字段>) as 最大值,min(<字符型字段>) as 最小值 from tb_name; -- 统计记录总数。 select count(*) as 总数 from tb_name; -- 统计指定字段值不为空的记录个数 select count(<字符型字段>) as 总数 from tb_name;对查询结果分组-- group by子句用于将查询结果表按某一列或多列值进行分组,列值相等的为一组,每组统计出一个结果。该子句常与统计函数一起使用进行分组统计。格式为: -- group by 分组字段[,...n][having <条件表达式>]; -- 1.在使用group by子句后 -- select列表中只能包含:group by子句中所指定的分组字段及统计函数。 -- 2.having子句的用法 -- having子句必须与group by 子句配合使用,用于对分组后的结果进行筛选(筛选条件中常含有统计函数)。 -- 3. 分组查询时不含统计函数的条件 -- 通常使用where子句;含有统计函数的条件,则只能用having子句。 select <字符型字段>,count(*) as 列标题 from tb_name where <字符型字段>='字符串' group by <字符型字段>;5.3 修改数据(Update)update tb_name set 列名称 = 新值 where 列名称 = 某值;5.4 删除数据(Delete)-- 条件删除 delete from tb_name where 列名称 = 某值; -- 删除所有行 delete * from tb_name; -- 或 delete from tb_name;参考资料mysql修改root密码和设置权限MySQL允许root远程登录MySQL基础 — 常用命令
2022年01月11日
897 阅读
0 评论
0 点赞
2022-01-08
语音识别:使用torchaudio快速实现音频特征提取
1.fbank特征import torch.nn as nn import torchaudio class ExtractAudioFeature(nn.Module): def __init__(self, feat_type="fbank", feat_dim=40): super(ExtractAudioFeature, self).__init__() self.feat_type = feat_type self.extract_fn = torchaudio.compliance.kaldi.fbank if feat_type == "fbank" else torchaudio.compliance.kaldi.mfcc self.num_mel_bins = feat_dim def forward(self, filepath): waveform, sample_rate = torchaudio.load(filepath) y = self.extract_fn(waveform, num_mel_bins=self.num_mel_bins, channel=-1, sample_frequency=sample_rate, frame_length=25, #每帧的时长 frame_shift=10, dither=0) return y.transpose(0, 1).unsqueeze(0).detach() extracter = ExtractAudioFeature("fbank",feat_dim=40) wav = "./data/wav/day0914_990.wav" wav_feature = extracter(wav) print(wav_feature.shape)torch.Size([1, 40, 489]) # 40:特征维度 # 489:音频帧数=音频时长/25ms查看图示import matplotlib.pyplot as plt plt.figure(dpi=200) plt.xticks([]) plt.yticks([]) plt.imshow(wav_feature[0]) plt.show()2.mfcc特征import torch.nn as nn import torchaudio class ExtractAudioFeature(nn.Module): def __init__(self, feat_type="mfcc", feat_dim=13): super(ExtractAudioFeature, self).__init__() self.feat_type = feat_type self.extract_fn = torchaudio.compliance.kaldi.fbank if feat_type == "fbank" else torchaudio.compliance.kaldi.mfcc self.num_mel_bins = feat_dim def forward(self, filepath): waveform, sample_rate = torchaudio.load(filepath) y = self.extract_fn(waveform, num_mel_bins=self.num_mel_bins, channel=-1, sample_frequency=sample_rate, frame_length=25, #每帧的时长 frame_shift=10, dither=0) return y.transpose(0, 1).unsqueeze(0).detach() extracter = ExtractAudioFeature("mfcc",feat_dim=13) wav = "./data/wav/day0914_990.wav" wav_feature = extracter(wav) print(wav_feature.shape)torch.Size([1, 13, 489]) # 13:特征维度 # 489:音频帧数=音频时长/25ms查看图示import matplotlib.pyplot as plt plt.figure(dpi=200) plt.xticks([]) plt.yticks([]) plt.imshow(wav_feature[0]) plt.show()参考资料https://github.com/neil-zeng/asr
2022年01月08日
1,810 阅读
0 评论
0 点赞
2022-01-08
pytorch 逐层加载模型参数
pytorch 逐层加载模型参数1.现象描述当对模型结构的某些层做了微小调整之后,导致该层参数的shape发生了微小变化导致无法对整个模型进行加载,这时可以考虑通过逐层加载的方式来跳过某些层完成对保存好的模型的加载。2.代码实现# 模型参数逐层加载 ckpt_state_dict = torch.load("ckpt/best_att_wer_0.015267175572519083.pth")["model"] model_state_dict = model.state_dict() for key in model_state_dict.keys(): if model_state_dict[key].shape == ckpt_state_dict[key].shape: model_state_dict[key] = ckpt_state_dict[key] model.load_state_dict(model_state_dict)
2022年01月08日
765 阅读
0 评论
0 点赞
2022-01-07
Yolov5s:Yolov5sv6.0网络结构分析与实现
1.参考网络结构图(v5.0的)2. 配置文件解析原始配置文件yolov5s.yaml# YOLOv5 by Ultralytics, GPL-3.0 license # Parameters nc: 80 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32 # YOLOv5 v6.0 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 9 ] # YOLOv5 v6.0 head head: [[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, Concat, [1]], # cat backbone P4 [-1, 3, C3, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 4], 1, Concat, [1]], # cat backbone P3 [-1, 3, C3, [256, False]], # 17 (P3/8-small) [-1, 1, Conv, [256, 3, 2]], [[-1, 14], 1, Concat, [1]], # cat head P4 [-1, 3, C3, [512, False]], # 20 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], # cat head P5 [-1, 3, C3, [1024, False]], # 23 (P5/32-large) [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) ]配置文件解析Model( (model): Sequential( (0): Conv(3,32,6,2,2) # 3x640x640-->32x320x320 (1): Conv(32,64,3,2) # 32x320x320-->64x160x160 (2): C3(64,64) # 64x160x160-->64x160x160 (3): Conv(64,128,3,2) # 64x160x160-->128x80x80 #P3 (4): C3(128,128) # 128x80x80-->128x80x80 (5): Conv(128,256,3,2) # 128x80x80-->256x40x40 #P4 (6): C3(256,256) # 256x40x40-->256x40x40 (7): Conv(256,512,3,2) # 256x40x40-->512x20x20 #P5 (8): SPP(512,512,[5, 9, 13]) # 512x20x20-->512x20x20 (9): C3(512,512) # 512x20x20-->512x20x20 #P6 (10): Conv(512,256,1,1) # 512x20x20-->256x20x20 (11): nn.Upsample(None, 2, 'nearest') # 256x20x20-->256x40x40 (12): Concat() # [x,p4]==>512x40x40 (13): C3(512,256) # 512x40x40-->256x40x40 (14): Conv(256,128) # 256x40x40-->128x40x40 (15): nn.Upsample(None, 2, 'nearest') # 128x40x40-->128x80x80 (16): Concat() # [x,p3]==>256x80x80 (17): C3(256,128) # 256x80x80-->128x80x80 #out1 (18): Conv(128,128,3,2) # 128x80x80-->128x40x40 (19): Concat() # [x,p4]==>384x40x40 (20): C3(384,256) # 384x40x40-->256x40x40 #out2 (21): Conv(256,256,3,2) # 256x40x40-->256x20x20 (22): Concat() # [x,p5]==>768x20x20 (23): C3(768,512) # 768x20x20 -->512x20x20 #out3 (24): Detect( (0): Conv2d(128, 255) # 128x80x80-->((cls_num+4+1)*anchor_num)x80x80 #out1_detect==>[3, 80, 80, 85] (1): Conv2d(256, 255) # 256x40x40-->((cls_num+4+1)*anchor_num)x40x40 #out2_detect==>[3, 40, 40, 85] (2): Conv2d(512, 255) # 512x20x20-->((cls_num+4+1)*anchor_num)x20x20 #out3_detect==>[3, 20, 20, 85] ) ) )3.代码实现3.1 公共基本块import torch import torch.nn as nn import warnings class Conv(nn.Module): # 标准卷积 def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups super().__init__() self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) self.bn = nn.BatchNorm2d(c2) self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) def forward(self, x): return self.act(self.bn(self.conv(x))) def forward_fuse(self, x): return self.act(self.conv(x)) def forward_fuse(self, x): return self.act(self.conv(x)) class Bottleneck(nn.Module): # 标准bottleneck def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion super().__init__() c_ = int(c2 * e) # hidden channels self.cv1 = Conv(c1, c_, 1, 1) self.cv2 = Conv(c_, c2, 3, 1, g=g) self.add = shortcut and c1 == c2 def forward(self, x): return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x)) class C3(nn.Module): # CSP Bottleneck with 3 convolutions def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion super().__init__() c_ = int(c2 * e) # hidden channels self.cv1 = Conv(c1, c_, 1, 1) self.cv2 = Conv(c1, c_, 1, 1) self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2) self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)]) def forward(self, x): return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1)) class SPPF(nn.Module): # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13)) super().__init__() c_ = c1 // 2 # hidden channels self.cv1 = Conv(c1, c_, 1, 1) self.cv2 = Conv(c_ * 4, c2, 1, 1) self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2) def forward(self, x): x = self.cv1(x) with warnings.catch_warnings(): warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning y1 = self.m(x) y2 = self.m(y1) return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1)) class Concat(nn.Module): # 沿维度连接张量列表 def __init__(self, dimension=1): super().__init__() self.d = dimension def forward(self, x): return torch.cat(x, self.d) def autopad(k, p=None): # kernel, padding # 计算然卷积结果与输入具有相同大小的padding if p is None: p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad return p3.2 backboneclass Yolov5sV6Backbone(nn.Module): def __init__(self): super(Yolov5sV6Backbone,self).__init__() self.backbone_part1 = nn.Sequential( Conv(3,32,6,2,2), # 0 Conv(32,64,3,2), # 1 C3(64,64), #2 Conv(64,128,3,2) #3 ) self.backbone_part2 = nn.Sequential( C3(128,128), # 4_1 Conv(128,256,3,2) # 5 ) self.backbone_part3 = nn.Sequential( C3(256,256), # 6 Conv(256,512,3,2) # 7 ) self.backbone_part4 = nn.Sequential( C3(512,512), # 8 SPPF(512,512,5), # 9 ) def forward(self,x): p3 = self.backbone_part1(x) p4 = self.backbone_part2(p3) p5 = self.backbone_part3(p4) p6 = self.backbone_part4(p5) return p3,p4,p5,p6调用测试backbone = Yolov5sV6Backbone() fake_input = torch.rand(1,3,640,640) p3,p4,p5,p6 = backbone(fake_input) print(p3.shape,p4.shape,p5.shape,p6.shape)torch.Size([1, 128, 80, 80]) torch.Size([1, 256, 40, 40]) torch.Size([1, 512, 20, 20]) torch.Size([1, 512, 20, 20])3.3 headclass Yolov5sV6Head(nn.Module): def __init__(self): super(Yolov5sV6Head,self).__init__() self.head_part1 = nn.Sequential( Conv(512,256,1,1), # 10 nn.Upsample(None, 2, 'nearest') # 11 ) self.head_concat1 =Concat() # 12 self.head_part2 = nn.Sequential( C3(512,256), # 13 Conv(256,128), # 14 nn.Upsample(None, 2, 'nearest') # 15 ) self.head_concat2 = Concat() # 16 self.head_out1 = C3(256,128) # 17 # 128x80x80 self.head_part3 = Conv(128,128,3,2) # 18 self.head_concat3 = Concat() # 19 self.head_out2 = C3(384,256) # 20 self.head_part4 = Conv(256,256,3,2) # 21 self.head_concat4 = Concat() # 22 self.head_out3 = C3(768,512) # 23 # 512x40x40 def forward(self,p3,p4,p5,x): x = self.head_part1(x) x = self.head_concat1([x,p4]) x = self.head_part2(x) x = self.head_concat2([x,p3]) out1 = self.head_out1(x) x = self.head_part3(out1) x = self.head_concat3([x,p4]) out2 = self.head_out2(x) x = self.head_part4(out2) x = self.head_concat4([x,p5]) out3 = self.head_out3(x) return out1,out2,out3调用测试backbone = Yolov5sV6Backbone() head = Yolov5sV6Head() fake_input = torch.rand(1,3,640,640) p3,p4,p5,p6 = backbone(fake_input) out1,out2,out3 = head(p3,p4,p5,p6) print(out1.shape,out2.shape,out3.shape)3.4 detect 部分class Yolov5sV6Detect(nn.Module): stride = None # strides computed during build def __init__(self, nc=80, anchors=(), ch=[128,256,512], inplace=True): # detection layer super(Yolov5sV6Detect,self).__init__() self.nc = nc # number of classes self.no = nc + 5 # number of outputs per anchor self.nl = len(anchors) # number of detection layers self.na = len(anchors[0]) // 2 # number of anchors self.grid = [torch.zeros(1)] * self.nl # init grid self.anchor_grid = [torch.zeros(1)] * self.nl # init anchor grid self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2)) # shape(nl,na,2) self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv self.inplace = inplace # use in-place ops (e.g. slice assignment) def forward(self, x): z = [] # inference output for i in range(self.nl): x[i] = self.m[i](x[i]) # conv bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() if not self.training: # inference if self.grid[i].shape[2:4] != x[i].shape[2:4]: self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i) y = x[i].sigmoid() if self.inplace: y[..., 0:2] = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh else: # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953 xy = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh y = torch.cat((xy, wh, y[..., 4:]), -1) z.append(y.view(bs, -1, self.no)) return x if self.training else (torch.cat(z, 1), x) def _make_grid(self, nx=20, ny=20, i=0): d = self.anchors[i].device if check_version(torch.__version__, '1.10.0'): # torch>=1.10.0 meshgrid workaround for torch>=0.7 compatibility yv, xv = torch.meshgrid([torch.arange(ny, device=d), torch.arange(nx, device=d)], indexing='ij') else: yv, xv = torch.meshgrid([torch.arange(ny, device=d), torch.arange(nx, device=d)]) grid = torch.stack((xv, yv), 2).expand((1, self.na, ny, nx, 2)).float() anchor_grid = (self.anchors[i].clone() * self.stride[i]) \ .view((1, self.na, 1, 1, 2)).expand((1, self.na, ny, nx, 2)).float() return grid, anchor_grid调用测试anchors = [ [10,13, 16,30, 33,23], [30,61, 62,45, 59,119], [116,90, 156,198, 373,326] ] backbone = Yolov5sV6Backbone() head = Yolov5sV6Head() detect = Yolov5sV6Detect(nc=80,anchors=anchors) fake_input = torch.rand(1,3,640,640) p3,p4,p5,p6 = backbone(fake_input) out1,out2,out3 = head(p3,p4,p5,p6) out1,out2,out3 = detect([out1,out2,out3]) print(out1.shape,out2.shape,out3.shape)torch.Size([1, 3, 80, 80, 85]) torch.Size([1, 3, 40, 40, 85]) torch.Size([1, 3, 20, 20, 85])3.5 整体组装class Yolov5sV6(nn.Module): def __init__(self,nc=80,anchors=()): super(Yolov5sV6,self).__init__() self.backbone = Yolov5sV6Backbone() self.head = Yolov5sV6Head() self.detect = Yolov5sV6Detect(nc,anchors) def forward(self,x): p3,p4,p5,p6 = self.backbone(x) out1,out2,out3 = self.head(p3,p4,p5,p6) out1,out2,out3 = self.detect([out1,out2,out3]) return out1,out2,out3调用测试anchors = [ [10,13, 16,30, 33,23], [30,61, 62,45, 59,119], [116,90, 156,198, 373,326] ] yolov5s = Yolov5sV6(nc=80,anchors=anchors) fake_input = torch.rand(1,3,640,640) out1,out2,out3 = yolov5s(fake_input) print(out1.shape,out2.shape,out3.shape)torch.Size([1, 3, 80, 80, 85]) torch.Size([1, 3, 40, 40, 85]) torch.Size([1, 3, 20, 20, 85])4.模型复杂度分析模型名Input模型大小全精度模型大小半精度参数量FLOPSbackbone640x64026.0MB13.1MB3.80M4.42GFLOPShead640x64011.5MB5.78MB3.00M2.79GFLOPSdetect640x640897KB450KB0.23M0.37GFLOPSYolov5s640x64026.9M13.5M7.03M7.58GFLOPS参考资料https://github.com/ultralytics/yolov5YOLOv5代码详解(common.py部分)Yolov5从入门到放弃(一)---yolov5网络架构YOLOV5网络结构
2022年01月07日
2,461 阅读
0 评论
0 点赞
2022-01-05
Subsonic:使用VPS自建私有云音乐服务
1.简介Subsonic是一个免费且基于Web的媒体流媒体,用Java编写,适用于Linux、MacOS和Windows平台。使用Subsonic,你可以从家用计算机或任何面向公众的计算机流式传输音乐,并通过网络浏览器随时随地聆听音乐,因此你无需将音乐与弹性同步或Syncthing等文件同步应用程序同步。Subsonic功能:支持MP3、OGG、AAC以及通过HTTP流式传输的任何其他音频或视频格式适用于任何支持网络的媒体播放器,如Winamp、iTunes、XMMS、VLC、MusicMatch和Windows Media Player。专辑艺术展示,即时播放列表,即时转码。适用于Mac、Windows、Android、iPhone、Windows Phone和桌面应用的移动应用,Android应用支持离线播放。收听播客,分配评级,添加评论和创建播放列表。与朋友和家人分享音乐。流视频(高级功能)。2.在Ubuntu 上安装Subsonic2.1 安装java环境sudo apt update sudo apt install openjdk-8-jre当前Subsonic版本与Java 11不兼容,如果之前在Ubuntu 18.04上安装了Java 11,那么需要运行以下命令来选择默认的Java版本sudo update-alternatives --config java2.2 安装Subsonic接下来,使用以下命令下载Subsonic deb软件包,该命令将下载6.1.6版,可以在Subsonic网站的下载页面上查看最新版本,如果有新版本,只需将6.1.6替换为新版本号即可:wget https://s3-eu-west-1.amazonaws.com/subsonic-public/download/subsonic-6.1.6.deb用dpkg安装它:sudo dpkg -i subsonic-6.1.5.deb安装后,Subsonic守护程序将自动启动2.3 管理Subsonic# 可以通过以下命令检查其状态: systemctl status subsonic # 如果它没有运行,那么你可以手动启动它: sudo systemctl start subsonic # 并在系统启动时启用自动启动: sudo systemctl enable subsonic2.4 访问测试并进行相关设置登录默认情况下,subsonic侦听0.0.0.0:4040,这意味着它接受来自本地网络和Internet的请求。如果在本地Ubuntu计算机上安装了Subsonic,请在浏览器中输入http://localhost:4040访问Subsonic Web界面:如果在面向Internet的Ubuntu服务器上安装了Subsonic,请在浏览器中输入http://your-server-ip:4040访问Subsonic Web界面:默认用户名和密码是admin修改语言修改媒体文件夹请注意,用户必须可以访问该文件夹添加文件夹后,单击“立即扫描媒体文件夹”按钮,将可以在“Index”页面中收听音乐:2.5 更改启动用户默认情况下,Subsonic进程以root用户身份运行,出于安全原因,应该将其更改为普通用户,这可以通过编辑/etc/default/subsonic文件来完成:sudo vim /etc/default/subsonic找到以下行:SUBSONIC_USER=root将root更改为自己的用户帐户,如jupiter:SUBSONIC_USER=jupiter保存并关闭文件,然后重新启动subsonic守护程序以使更改生效:sudo systemctl restart subsonic2.在Centos 上安装Subsonic参考:https://zhuanlan.zhihu.com/p/46767211参考资料使用VPS自建私有云音乐服务–Subsonic在Ubuntu 18.04系统上安装Subsonic的方法
2022年01月05日
2,546 阅读
0 评论
1 点赞
2021-12-30
端侧CNN参数压缩和加速(模型轻量化)技术
1.简介1.1 应用背景与云端的GPU设备相比,端侧的设备在成本和功耗方面具有明显优势。但在端侧的设备具有更小的内存,更小的算力,这对移动端的模型带来了更高的要求,端测模型需要满足模型尺寸小、计算复杂度低、电池耗电量低、部署灵活等条件。设备类型设备名显存/内存浮点算力GPURTX 308010G29117 GFLOPSGPURTX 2080 Ti11G16626 GFLOPS端侧设备树莓派4B2G/4G/8G13.5 GFLOPS1.2 模型压缩和加速介绍模型压缩和加速是两个不同的话题,压缩重点在于减少网络参数量,加速则侧重在降低计算复杂度、提升并行能力等。有时候压缩并不一定能带来加速的效果,有时候又是相辅相成的。模型压缩和加速可以从多个角度来优化。算法层压缩加速。这个维度主要在算法应用层,也是大多数算法工程师的工作范畴。主要包括结构优化(如矩阵分解、分组卷积、小卷积核等)、量化与定点化、模型剪枝、模型蒸馏等。框架层加速。这个维度主要在算法框架层,比如tf-lite、NCNN、MNN等。主要包括编译优化、缓存优化、稀疏存储和计算、NEON指令应用、算子优化等硬件层加速。这个维度主要在AI硬件芯片层,目前有GPU、FPGA、ASIC等多种方案,各种TPU、NPU就是ASIC这种方案,通过专门为深度学习进行芯片定制,大大加速模型运行速度。本文将重点针对算法层压缩加速进行介绍。2.算法层压缩加速2.1 轻量架构设计/结构优化2.1.1 分组卷积在分组卷积(Group convolution)中,输入特征图尺寸为 $H \times W \times c_1$,将输入特征图按照通道数分成 $g$组,则每组输入特征图的尺寸为$H \times W \times \frac{c_1}{g}$,对应的卷积核尺寸为 $h_1 \times w_1 \times \frac{c_1}{g}$ ,每组输出特征图尺寸为$H \times W \times \frac{c_2}{g}$,将$g$组结果拼接(concat),得到最终输出特征图尺寸为 $H \times W \times c_2$ ,因此分组卷积层的参数量为:$$ h_1 \times w_1 \times \frac{c_1}{g} \times \frac{c_2}{g} \times g = h_1 \times w_1 \times c_1 \times c_2 \times \frac{1}{g} $$即分组卷积的参数量是标准卷积的$\frac{1}{g}$。2.1.2 MobileNet结构MobileNetv1 论文该论文最大的创新点是,提出了深度可分离卷积(depthwise separable convolution)。同时作者还使用了ReLU6激活函数来替代ReLU激活函数。标准卷积在标准卷积中,输入特征图尺寸为 $H \times W \times c_1$,卷积核尺寸为 $h_1 \times w_1 \times c_1$ ,输出特征图尺寸为 $H \times W \times c_2$ ,标准卷积层的参数量为:$$ (h_1 \times w_1 \times c_1) \times c_2 $$运算量约为(忽略偏置计算):$$ w \times h \times c \times w_1 \times h_1 \times c_1 $$深度可分离卷积深度可分离卷(Depthwise separable convolution)积由逐通道卷积和逐点卷积两个部分组合而成,用来提取特征feature map。相比常规的卷积操作,其参数数量和运算成本比较低。逐通道卷积逐通道卷积(Depthwise Convolution)的一个卷积核负责一个通道,一个通道只被一个卷积核卷积。该阶段等价于$g=c_1$的组卷积,其参数量为:$$ h_1 \times w_1 \times 1 \times c_1 $$计算量为:$$ h_1 \times w_1 \times c_1 \times h_2 \times w_2 $$逐点卷积逐点卷积(Pointwise Convolution)的运算与常规卷积运算非常相似,它的卷积核的尺寸为 1×1×M,M为上一层的通道数。所以这里的卷积运算会将上一步的map在深度方向上进行加权组合,生成新的Feature map。有几个卷积核就有几个输出Feature map该阶段按普通卷积的方式计算可得参数量为:$$ 1 \times 1 \times c_1 \times c_2 $$计算量为:$$ h_2 \times w_2 \times c_1 \times c_2 $$总参数量与计算量分析则总的参数量为:$$ h_1 \times w_1 \times c_2 + c_1 \times c_2 $$即深度可分离卷积的参数量是标准卷积的$$ \frac{h_1 \times w_1 \times c_1 + c_1 \times c_2}{(h_1 \times w_1 \times c_1) \times c_2} = \frac{1}{c_2}+\frac{1}{h_1 \times w_1} $$总的计算量为:$$ h_1 \times w_1 \times c_1 \times h_2 \times w_2 +h_2 \times w_2 \times c_1 \times c_2 $$即深度可分离卷积的的计算量是标准卷积的:$$ \frac{h_1 \times w_1 \times c_1 \times h_2 \times w_2 +h_2 \times w_2 \times c_1 \times c_2}{h_1 \times w_1 \times c_1 \times h_2 \times w_2} = \frac{1}{c_2}+\frac{1}{h_1 \times w_1} $$ReLU6$$ ReLU = max(0,x) \\ ReLU6 = max(max(0,x),6) $$上图左边是普通的ReLU,对于大于0的值不进行处理,右边是ReLU6,当输入的值大于6的时候,返回6,relu6“具有一个边界”。作者认为ReLU6作为非线性激活函数,在低精度计算下具有更强的鲁棒性。(这里所说的“低精度”,我看到有人说不是指的float16,而是指的定点运算(fixed-point arithmetic))MobileNetv2 论文有人在实际使用深度可分离卷积的时候, 发现深度卷积部分的卷积核比较容易训废掉:训完之后发现深度卷积训出来的卷积核有不少是空的(参数都为0了),作者认为这是ReLU激活函数的锅。因此,作者将V2在V1的基础上,引入了Inverted Residuals(倒残差模块)和Linear Bottlenecks。简单来说,就是当低维信息映射到高维,经过ReLU后再映射回低维时,若映射到的维度相对较高,则信息变换回去的损失较小;若映射到的维度相对较低,则信息变换回去后损失很大,如下图所示。因此,作者认为对低维度做ReLU运算,很容易造成信息的丢失。而在高维度进行ReLU运算的话,信息的丢失则会很少。另外一种解释是,高维信息变换回低维信息时,相当于做了一次特征压缩,会损失一部分信息,而再进过ReLU后,损失的部分就更加大了。作者为了这个问题,就将ReLU替换成线性激活函数。Inverted Residuals/倒残差模块残差模块:输入首先经过$1 \times 1$的卷积进行压缩,然后使用$3 \times 3$的卷积进行特征提取,最后在用$1 \times 1$的卷积把通道数变换回去。整个过程是“压缩-卷积-扩张”。这样做的目的是减少$3 \times 3$模块的计算量,提高残差模块的计算效率。倒残差模块:输入首先经过$1 \times 1$的卷积进行通道扩张,然后使用$3 \times 3$的depthwise卷积,最后使用$1 \times 1$的pointwise卷积将通道数压缩回去。整个过程是“扩张-卷积-压缩”。为什么这么做呢?因为depthwise卷积不能改变通道数,因此特征提取受限于输入的通道数,所以将通道数先提升上去。文中的扩展因子为6。Linear Bottlenecks这个模块是为了解决上面提出的那个低维-高维-低维的问题,即将最后一层的ReLU替换成线性激活函数,而其他层的激活函数依然是ReLU6。如下图所示。当stride=1时,输入首先经过$1 \times 1$的卷积进行通道数的扩张,此时激活函数为ReLU6;然后经过$3 \times 3$的depthwise卷积,激活函数是ReLU6;接着经过$1 \times 1$的pointwise卷积,将通道数压缩回去,激活函数是linear;最后使用shortcut,将两者进行相加。而当stride=2时,由于input和output的特征图的尺寸不一致,所以就没有shortcut了。MobileNetv3 论文mobilenet V3并没有惊艳的结构提出,主要是一些tricks的应用和结合。v3版本结合了v1的深度可分离卷积、v2的Inverted Residuals和Linear Bottleneck、h-swish非线性变换、SE模块,利用NAS(神经结构搜索)来搜索网络的配置和参数。引入SE结构在bottlenet结构中加入了SE结构,并且放在了depthwise filter之后,如下图:因为SE结构会消耗一定的时间,所以作者在含有SE的结构中,将expansion layer的channel变为原来的1/4,这样作者发现,即提高了精度,同时还没有增加时间消耗。修改尾部结构在mobilenetv2中,在avg pooling之前,存在一个$1 \times 1$的卷积层,目的是提高特征图的维度,更有利于结构的预测,但是这其实带来了一定的计算量了,所以这里作者修改了,将其放在avg pooling的后面,首先利用avg pooling将特征图大小由$7 \times 7$降到了$1 \times 1$,降到$1 \times 1$后,然后再利用$1 \times 1$提高维度,这样就减少了$7 \times 7=49$倍的计算量。为了进一步的降低计算量,作者直接去掉了前面纺锤型卷积的$3 \times 3$以及$1 \times 1$卷积,进一步减少了计算量,就变成了如上图第二行所示的结构,作者将其中的$3 \times 3$以及$1 \times 1$去掉后,精度并没有得到损失。这里降低了大约15ms的速度。修改channel数量修改头部卷积核channel数量,mobilenet v2中使用的是$32 \times 3 \times 3$,作者发现,其实32可以再降低一点,所以这里作者改成了16,在保证了精度的前提下,降低了3ms的速度。非线性变换(新激活函数)的改变谷歌提出了一种新出的激活函数swish x(如下) 能有效改进网络精度,但是计算量太大了。$$ swish(x)=x \cdot \sigma(x) = x \cdot sigmoid(x) $$作者使用h-swish来近似替代sigmoid,进行了速度优化,具体如下:$$ h-swish(x)=x \frac{ReLU6(x+3)}{6} $$利用ReLU有几点好处:1.可以在任何软硬件平台进行计算2.量化的时候,它消除了潜在的精度损失,使用h-swish替换swish,在量化模式下回提高大约15%的效率,另外,h-swish在深层网络中更加明显。2.1.3 IGC结构IGCv1 论文它的重点在于提出了一个新颖的构建块--交错组卷积。交错组卷积它是一种交错组卷积块的堆叠结构。每个块包含两个组卷积: 第一次组卷积核第二次组卷积,所谓组卷积就是在卷积时有两个分组。第一次组卷积是把输入通道划分为L个组,每个组包含M个通道,然后将L个分组卷积的结果拼接在一起得到新的输入其仍然有LxM个通道之后打乱顺序第二次组卷积划分了M个组,每个组包含L个通道,且这L个通道来自第一次分组卷积时的不同组。同时第一次卷积做的是空间域卷积,第二次做的是逐点卷积。比起普通卷积,在网络参数量和计算复杂度不变的同时,使得网络变得更宽了。IGCv1 vs XceptionIGCV是第一个空间域的$3 \times 3$Group Conv,之后接第二个$1 \times 1$逐点卷积来融合通道信息,乍一看和Xception很像,可以说Xception就是IGCV的一种极端情况,Group数和Channel数相等的情况。作者列举了$L=1$以及$M=1$的极端情况,也就对应着普通卷积以及Xception的极端组卷积结果当然是IGCv1最好(摊手),从图上可以看到当每组只有2个通道的时候,效果是最好的,也就是文中最后IGCv1的结构了。最后还补充了两次组卷积次序可以交换,并不影响结果;可以将$3 \times 3$卷积分解成$3 \times 1$和$1 \times 3$,可以更加提高效率。IGCv2 论文IGCv2的主要创新点也就是在IGCv1的基础上,对于block里第二次组卷积再进行一次IGC。即使用多个稀疏卷积来替换原本比较稠密的卷积。IGCv1 vs IGCv2 IGCv1的结构如上图,可以看出第二次组卷积的时候,每一组的通道数仍然很多,文中的话就是比较dense, 因此想到对于每一组再进行IGC,这样就能进一步提升计算的效率了,也就是IGCv2,将每个组的普通卷积替换成交错组卷积,具体可见下图,IGCv3 论文IGCV3的主要创新点对IGCV2的结构进行延伸,引入低秩分组卷积来代替原本的分组卷积。就是先用1x1Group Pointwise Covolution升维,再3x3的组卷积,再通过1x1Group Pointwise Covolution进行降维,主要结构如下图与MobileNetV2的对比,基本上就是1x1卷积改成了1x1的组卷积。IGCV3在IGCV2的基础上融合了MobileNetV2的主要结构,并且使用更狠的低秩稀疏分组卷积,在整体结构上和MobileNetV2十分接近,核心依然是在稀疏分组卷积以及排序操作,虽然性能比MobileNetV2有些许提升,但整体的创新性略显不足。2.1.4 Inception结构Inception结构作用原理:Inception的作用就是替代了人工确定卷积层中过滤器的类型或者是否创建卷积层和池化层,让网络自己学习它具体需要什么参数。Inception-v1 论文提高网络最简单粗暴的方法就是提高网络的深度和宽度,即增加隐层和以及各层神经元数目。但这种简单粗暴的方法存在一些问题:会导致更大的参数空间,更容易过拟合需要更多的计算资源网络越深,梯度容易消失,优化困难(这时还没有提出BN时,网络的优化极其困难)Inception-v1的目标就是,提高网络计算资源的利用率,在计算量不变的情况下,提高网络的宽度和深度。首先看第一个结构,有四个通道,有$1 \times 1、3 \times 3、5 \times 5$卷积核,该结构有几个特点:采用大小不同的卷积核,意味着感受野的大小不同,就可以得到不同尺度的特征。采用比较大的卷积核即$5 \times 5$,因为有些相关性可能隔的比较远,用大的卷积核才能学到此特征。但是这个结构有个缺点,$5 \times 5$的卷积核的计算量太大。那么作者想到了第二个结构,用$1 \times 1$的卷积核进行降维。这个1*1的卷积核,它的作用就是:降低维度,减少计算瓶颈增加网络层数,提高网络的表达能力那么在具体的卷积神经网络中,Inception应该放在哪里,作者的建议,在底层保持传统卷积不变,在高层使用Inception结构。Inception-v2 论文其主要思想在于提出了Batch Normalization,其次才是稍微改进了一下Inception,把Inception-v1中$5 \times 5$的卷积用2个$3 \times 3$的卷积替换。这样做法有两个优点:保持相同感受野的同时减少参数加强非线性的表达能力Inception-v3 论文主要从分解卷积核尺寸、使用辅助分类器、改变降低特征图尺寸的方式三个方面进行改进(使用辅助分类器与轻量化设计关系不大)。分解卷积核尺寸分解为对称的小的卷积核,(参考上面的Inception-v2)分解为不对称的卷积核分解为不对称的卷积核就是将$n \times n$的卷积核替换成 $1 \times n$ 和$n \times 1$ 的卷积核堆叠,计算量又会降低。但是该方法在大维度的特征图上表现不好,在特征图12-20维度上表现好。不对称分解方法有几个优点:节约了大量的参数增加一层非线性,提高模型的表达能力可以处理更丰富的空间特征,增加特征的多样性改变降低特征图尺寸的方式在传统的卷积神经网络的中,当有pooling时(pooling层会大量的损失信息),会在之前增加特征图的厚度(就是双倍增加滤波器的个数),通过这种方式来保持网络的表达能力,但是计算量会大大增加。作者对传统的池化方式进行了如下的改进:有两个通道,一个是卷积层,一个是pooling层,两个通道生成的特征图大小一样,concat在一起即可。Inception-v4 [论文]()本文的主要思想是将Inception与很火的ResNet结合,与轻量化设计的关系不大。2.1.5 Xception结构Xception 论文Xception 是 Google 继 Inception 后提出的对 Inception-v3 的另一种改进。作者认为,通道之间的相关性 与 空间相关性 最好要分开处理。采用 Separable Convolution(极致的 Inception 模块)来替换原来 Inception-v3中的卷积操作。结构的变形过程如下:在 Inception 中,特征可以通过$1 \times 1$卷积, $3 \times 3$卷积, $5 \times 5$ 卷积,pooling 等进行提取,Inception 结构将特征类型的选择留给网络自己训练,也就是将一个输入同时输给几种提取特征方式,然后做 concat 。Inception-v3的结构图如下:对 Inception-v3 进行简化,去除 Inception-v3 中的 avg pool 后,输入的下一步操作就都是 $1 \times 1$卷积:提取$1 \times 1$卷积的公共部分:Xception(极致的 Inception):先进行普通卷积操作,再对$1 \times 1$卷积后的每个channel分别进行$3 \times 3$卷积操作,最后将结果 concat:2.1.6 ResNeXt结构ResNeXt 论文ResNeXt是ResNet和Inception的结合体,不同于Inception v4的是,ResNext不需要人工设计复杂的Inception结构细节,而是每一个分支都采用相同的拓扑结构,ResNeXt的本质是分组卷积。所有Inception模型都具有一个重要的性质——都是遵循 拆分-变换-合并(split-transform-merge) 的设计策略。Inception模型中block的输入会先被拆分成若干低维编码(使用$1 \times 1$卷积实现),然后经过多个不同的滤波器(如$3 \times 3$、$5 \times 5$等)进行转换,最后通过沿通道维度串联的方式合并。这种设计策略希望在保持网络计算复杂度相当低的前提下获取与包含大量且密集的层的网络具有相同的表示能力。但是,Inception模型实现起来很麻烦,它包含一系列复杂的超参——每个变换的滤波器的尺寸和数量都需要指定,不同阶段的模块也需要定制。太多的超参数大多的影响因素,如何将Inception模型调整到适合不同的数据集/任务变得很不明晰。简化Inception基于此,作者的思想是每个结构使用相同的拓扑结构,那么这时候的Inception(这里简称简化Inception)表示为:$$ y = \sum^{C}_{i=1}T_i(x) $$其中 $C$ 是简Inception的基数(Cardinality),$T_i$是任意的变换,例如一系列的卷积操作等。Inception简化前后的对比如下图所示:ResNeXt再结合强大的残差网络,我们得到的便是完整的ResNeXt,也就是在简化Inception中添加一条short-cut,表示为:$$ y = \sum^{C}_{i=1}T_i(x)+x $$如下图所示:与Inception v4的区别ResNeXt的分支的拓扑结构是相同的,Inception V4需要人工设计;ResNeXt是先进行 $1 \times 1$卷积然后执行单位加,Inception V4是先拼接再执行 $1 \times 1$ 卷积。2.1.7 ShuffleNet结构ShuffleNet v1 [论文]()作者通过分析Xception和ResNeXt模型,发现这两种结构通过卷积核拆分虽然计算复杂度均较原始卷积运算有所下降,然而拆分所产生的逐点卷积计算量却相当可观,成为了新的瓶颈。例如对于ResNeXt模型逐点卷积占据了93.4%的运算复杂度。可见,为了进一步提升模型的速度,就必须寻求更为高效的结构来取代逐点卷积。受ResNeXt的启发,作者提出使用分组逐点卷积(group pointwise convolution)来代替原来的结构。通过将卷积运算的输入限制在每个组内,模型的计算量取得了显著的下降。然而这样做也带来了明显的问题:在多层逐点卷积堆叠时,模型的信息流被分割在各个组内,组与组之间没有信息交换(如图 1(a)所示)。这将可能影响到模型的表示能力和识别精度。因此,在使用分组逐点卷积的同时,需要引入组间信息交换的机制。也就是说,对于第二层卷积而言,每个卷积核需要同时接收各组的特征作为输入,如上图(b)所示。作者指出,通过引入“通道重排”(channel shuffle,见上图(c))可以很方便地实现这一机制;并且由于通道重排操作是可导的,因此可以嵌在网络结构中实现端到端的学习。作者还观察到对于较小的网络(如ShuffleNet 0.25x),较大的分组会得到更好结果,认为更宽的通道对于小网络尤其重要。ShuffleNet v2 论文论文中提出了FLOPs不能作为衡量目标检测模型运行速度的标准,因为MAC(Memory access cost)也是影响模型运行速度的一大因素。由此,作者通过实验得出4个设计小模型的准则:卷积操作时,输入输出采用相同通道数可以降低MAC过多的组,会导致MAC增加分支数量过少,模型速度越快element-wise操作导致速度的消耗,远比FLOPs上体现的多。根据以上四点准则,作者在shuffleNet V1的基础上提出了修改得到了shuffleNet V2的基本快。2.1.8 GhostNet结构GhostNet 论文作者通过对比分析ResNet-50网络第一个残差组(Residual group)输出的特征图可视化结果,发现一些特征图高度相似(如Ghost一般,下图中的三组box内的图像对)。如果按照传统的思考方式,可能认为这些相似的特征图存在冗余,是多余信息,想办法避免产生这些高度相似的特征图。但本文思路清奇,推测CNN的强大特征提取能力和这些相似的特征图(Ghost对)正相关,不去刻意的避免产生这种Ghost对,而是尝试利用简单的线性操作来获得更多的Ghost对。常规卷积输入为$Y = w \times h \times c$,输出为$Y_1 = w_1 \times h_1 \times c_1$,运算量约为$w \times h \times c \times w_1 \times h_1 \times c_1$(忽略偏置计算)。Ghost ModuleGhost Module分为常规卷积、Ghost生成和特征图拼接三步1.首先用常规卷积得到本征特征图$Y'$(intrinsic feature maps),2.然后将本征特征图$Y'$每一个通道的特征图$y_i'$,用$\Phi_{i,j}$操作来产生Ghost特征图$y_{i,j}$对线性操作 $\Phi_{i,j}$的理解:论文中表示,可以探索仿射变换和小波变换等其他低成本的线性运算来构建Ghost模块。但是,卷积是当前硬件已经很好支持的高效运算,它可以涵盖许多广泛使用的线性运算,例如平滑、模糊等。 此外,线性运算 $\Phi_{i,j}$的滤波器的大小不一致将降低计算单元(例如CPU和GPU)的效率,所以论文中实验中让Ghost模块中的滤波器size取固定值,并利用Depthwise卷积实现 $\Phi_{i,j}$ ,以构建高效的深度神经网络。所以说,论文中使用的线性操作并不是常见的旋转、平移、仿射变换、小波变换等,而是用的Depthwise卷积。一种猜测可能是传统的线性操作效果没有Depthwise效果好,毕竟CNN可以自动调整filter的权值。3.最后将第一步得到的本征特征图和第二步得到的Ghost特征图拼接(identity连接)得到最终结果OutPut。当然也可以认为(论文中的思路),第2步中的$\Phi_{i,j}$中包含一个恒等映射,即将本征特征图直接输出,则不需用第三部,例如:对于$Y’$中的每一个特征图,对其进行$s$次映射,$s$次中包含一次恒等映射,其余$s-1$次为cheap operate来得到Ghost特征图,最终得到输出结果,这二者理论上完全一致。因此可以看出,Ghost Module和深度分离卷积是很类似的,不同之处在于先进行PointwiseConv,后进行DepthwiseConv,另外增加了DepthwiseConv的数量,包括一个恒定映射。2.1.9 SENet结构该结构与轻量化的关系不大,但是与提升整体的网络性能有关系,因此也放在这里一起进行介绍。SENet 论文SENet网络的创新点在于关注channel之间的关系,希望模型可以自动学习到不同channel特征的重要程度。论文通过显式地建模通道之间的相互依赖关系,自适应地重新校准通道的特征响应,从而设计了SE block如下图所示。论文的核心就是Squeeze和Excitation(论文牛的地方)两个操作。Squeeze(挤压)由于卷积只是在一个局部空间内进行操作,$U$(特征图)很难获得足够的信息来提取channel之间的关系,对于网络中前面的层这更严重,因为感受野比较小。为了,SENet提出Squeeze操作,将一个channel上整个空间特征编码为一个全局特征,采用global average pooling来实现(原则上也可以采用更复杂的聚合策略):$$ z_c = F_{sq}(u_c)=\frac{1}{H \times W}\sum^{H}_{i=1}\sum^{W}_{j=1}u_c(i,j),z \in R^C $$Excitation(激励)Sequeeze操作得到了全局描述特征,然后使用Excitation来抓取channel之间的关系。这个操作需要满足两个准则:首先要灵活,它要可以学习到各个channel之间的非线性关系;第二点是学习的关系不是互斥的,因为这里允许多channel特征,而不是one-hot形式。基于此,这里采用sigmoid形式的gating机制:$$ s =F_{ex}(z,W)=\sigma(g(z,W))=\sigma(W_2ReLU(W_1z)) $$为了降低模型复杂度以及提升泛化能力,这里采用包含两个全连接层的bottleneck结构,其中第一个FC层起到降维的作用,降维系数为r是个超参数,然后采用ReLU激活。最后的FC层恢复原始的维度。最后将学习到的各个channel的激活值(sigmoid激活,值0~1)乘以U上的原始特征(具体见下图SE模块在Inception和ResNet上的应用):$$ \tilde{x}c=Fscale(u_c,s_c)=s_c \cdot u_c $$整个操作可以看成学习到了各个channel的权重系数,从而使得模型对各个channel的特征更有辨别能力,这应该也算一种attention机制。SE模块在Inception和ResNet上的应用2.2 模型量化模型:特指卷积神经网络(用于提取图像/视频视觉特征)量化:将信号的连续取值(或者大量可能的离散取值)近似为有限多个(或较少的)离散值的过程。深度学习模型参数通常是32bit浮点型,我们能否使用16bit,8bit,甚至2bit来存储呢?答案是肯定的。这就是模型量化需要解决的问题。是什么阻碍了量化模型的落地?精度挑战大线性量化对分布的描述不精确8bit to 1bit:比特数越低,精度损失越大分类to检测to识别:任务越难,精度损失越大大模型to小模型:通常认为模型越小,精度损失越大Depthwise对量化不友好Merge BN, Fully Quantized软硬件支持程度不同硬件高效支持的低比特指令不一样不同软件库提供的量化方案/量化细节不一样不同软件库对于不同Layer的量化位置不一样速度能否变快不是所有的硬件上都提供高效的量化计算指令针对体系结构精细的优化不同结构优化方案不同系统&算法协同设计2.2.1 伪量化所谓伪量化是指在存储时使用低精度进行量化,但推理时会还原为正常高精度。为什么推理时不仍然使用低精度呢?这是因为一方面框架层有些算子只支持浮点运算,需要专门实现算子定点化才行。另一方面,高精度推理准确率相对高一些。伪量化可以实现模型压缩,但对模型加速没有多大效果。普通伪量化常见的做法是保存模型每一层时,利用低精度来保存每一个网络参数,同时保存拉伸比例scale(标度)和零值对应的浮点数zero_point(零点位置),量化公式如下:$$ Q(input,scale,zero\_point) = round(\frac{input}{scale}+zero\_point) $$推理阶段,再利用公式:$$ input = zero\_point + Q(input,scale,zero\_point) \times scale $$将网络参数还原为32bit浮点。eg:假设量化为qint8,设量化后的数Q为00011101,scale = 0.025 , zero_point = 0;最高位为0(符号位),所以是正数;后7位转换为10进制是29,所以Q代表的数为 :zero_point + Q scale = 0 + 29 0.025 = 0.725聚类与伪量化利用k-means等聚类算法,步骤如下:将大小相近的参数聚在一起,分为一类。每一类计算参数的平均值,作为它们量化后对应的值。每一类参数存储时,只存储它们的聚类索引。索引和真实值(也就是类内平均值)保存在另外一张表中推理时,利用索引和映射表,恢复为真实值。过程如下图所示,从上可见,当只需要4个类时,我们仅需要2bit就可以实现每个参数的存储了,压缩量达到16倍。推理时通过查找表恢复为浮点值,精度损失可控。2.2.2 定点化与伪量化不同的是,定点化在推理时,不需要还原为浮点数。这需要框架实现算子的定点化运算支持。目前NCNN、MNN、XNN等移动端AI框架中,均加入了定点化支持。2.2.3 其他角度分类二值化$$ Q_B(x)=sgn(x)=\begin{cases} +1,& if \ x \ge 0 \\ -1,& otherwise \end{cases} $$线性量化(对称、非对称、Ristretto)Quantize$$ x_{int} = round(\frac{x}{\Delta})+z \\ x_Q = clamp(0,N_{levels}-1,x_{int}) $$De-quantize$$ x_{float}=(x_Q-z)\Delta $$对数量化可将乘法转变为加法,加法转变为索引2.3 剪枝TODO参考资料理解分组卷积和深度可分离卷积如何降低参数量深度可分离卷积inception-v1,v2,v3,v4----论文笔记深度学习模型轻量化(上) ShuffleNet:一种极高效的移动端卷积神经网络 shuffleNet v1 v2笔记XceptionResNeXt详解ResNet系] 003 ResNeXt最后一届ImageNet冠军模型:SENetGhostNet论文解析:Ghost ModuleMobileNet系列轻量级神经网络“巡礼”(二)—— MobileNet,从V1到V3轻量化网络:MobileNet v3解析mobilenet系列之又一新成员---mobilenet-v3深度学习: 激活函数 (Activation Functions)轻量级网络:IGCV系列V1、V2、V3IGC系列:全分组卷积网络,分组卷积极致使用 | 轻量级网络pytorch量化中torch.quantize_per_tensor()函数参数详解深度学习模型轻量化(下) Szegedy C, Liu W, Jia Y, et al. Going deeper with convolutions[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2015: 1-9.Ioffe S, Szegedy C. Batch normalization: Accelerating deep network training by reducing internal covariate shift[C]//International conference on machine learning. PMLR, 2015: 448-456.Szegedy C, Vanhoucke V, Ioffe S, et al. Rethinking the inception architecture for computer vision[C]//Proceedings of the IEEE conference on computer vision and pattern recognition. 2016: 2818-2826.Szegedy C, Ioffe S, Vanhoucke V, et al. Inception-v4, inception-resnet and the impact of residual connections on learning[C]//Thirty-first AAAI conference on artificial intelligence. 2017.模型量化了解一下?【商汤泰坦公开课】模型量化的那些事
2021年12月30日
729 阅读
0 评论
0 点赞
2021-12-30
Linux下出现bash: /bin/xx: 参数列表过长
1.现象描述当目录下文件过多时,执行类似如下命令会出现bash: /bin/xx: 参数列表过长的错误# 列出当前目的下的文件到txt ls * >>../all.txt # 删除符合某种条件的文件 rm *.txt # 对于remove、cp、move等同样适用。2.解决方案使用xargs+find配合进行解决。举例如下:# ls find . -name "*"|xargs ls >>../all.txt find . -type f -name "*"|xargs ls >>../all.txt # rm find . -name "*.log"|xargs rm -rf "*.log" # 对于remove、cp、move等同样适用。参考资料Linux那点事-xargs命令详解bash: /bin/ls: 参数列表过长bash: /bin/rm: 参数列表过长
2021年12月30日
1,078 阅读
1 评论
0 点赞
2021-12-23
Code-server:一款基于VScode的在线编辑器
1.介绍code-server是一款基于VScode的在线编辑器,主要用于在Linux服务器环境下,实现任何设备通过浏览器即可访问VScode, 进而实现在远程编程.GIthub官方文档:https://github.com/cdr/code-server2.安装使用一键脚本安装curl -fsSL https://code-server.dev/install.sh | sh修改配置文件vim ~/.config/code-server/config.yaml# 对以下内容自行进行修改 bind-addr: 0.0.0.0:8080 auth: password password: jupiter cert: false使用screen后台运行screen -S code-server code-server打开IP:8080输入密码测试参考资料https://github.com/coder/code-server
2021年12月23日
1,159 阅读
1 评论
0 点赞
2021-12-23
SSHFS:通过ssh实现挂载远程目录到本地
1.简介SSHFS(SSH Filesystem)是一个基于FUSE的文件系统客户端,用于通过SSH连接远程目录。SSHFS使用的是SFTP协议,它是SSH的一个子系统,在大多数SSH服务器上默认启用与其他网络文件系统(如NFS和Samba)相比,SSHFS的优势在于它不需要在服务器端进行任何额外的配置。要使用SSHFS,您只需要SSH访问远程服务器。2.安装ubuntusudo apt install sshfscenteOSsudo yum -y install sshfs3.使用3.1 命令格式挂载sshfs [user@]host:[dir] mountpoint [options] # 常用option -p PORT #指定端口取消挂载umount mountpoint3.2 示例sshfs root@192.168.0.105:/Shares /mnt # 挂载 umount /mnt # 取消挂载如果需要开机自动挂载,可以在/etc/fstab文件中添加挂载项(前提是需要设置无密码登录,不然开机不能挂载):echo 'root@192.168.0.105:/Shares /mnt fuse.sshfs defaults 0 0'>> /etc/fstab参考资料使用SSHFS文件系统远程挂载目录Ubuntu下使用sshfs挂载远程目录到本地
2021年12月23日
925 阅读
0 评论
0 点赞
2021-12-22
Next Terminal:在浏览器中管理你的服务器
1.介绍Next Terminal是使用Golang和React开发的一款HTML5的远程桌面网关,具有小巧、易安装、易使用、资源占用小的特点,支持RDP、SSH、VNC和Telnet协议的连接和管理。Next Terminal基于 Apache Guacamoleopen in new window 开发,使用到了guacd服务。目前支持的功能有:授权凭证管理资产管理(支持RDP、SSH、VNC、TELNET协议)指令管理批量执行命令在线会话管理(监控、强制断开)离线会话管理(查看录屏)双因素认证资产标签资产授权多用户&用户分组计划任务2.docker安装#docker安装Next Terminal docker run -d \ -p 8088:8088 \ --name next-terminal \ --restart always dushixiang/next-terminal:latest安装完毕后访问IP:8088,用户名为admin,密码为admin,登录后可在后台进行修改。参考资料使用Next Terminal在浏览器中管理你的服务器https://next-terminal.typesafe.cn/
2021年12月22日
1,102 阅读
0 评论
0 点赞
2021-12-22
h5ai:一款功能强大 php 文件目录列表程序
1.介绍h5ai是一款功能强大 php 文件目录列表程序,不需要数据库,支持在线预览文本、图片、音频、视频等。h5ai功能包括:文件排序,不同视图模式,本地化,面包屑,树视图,自定义页眉和页脚,文件过滤器和搜索,文件夹大小,自动刷新,打包下载,QR码,缩略图,文件预览。官网:https://larsjung.de/h5ai/github:https://github.com/lrsjng/h5ai2.宝塔安装2.1 php扩展安装与函数解禁在安装之前,我们需要在php中安装ImageMagick、fileinfo、exif扩展和解禁putenv函数。2.2 创建网站2.3 源码下载从官网或者github下载程序包,解压然后将_h5ai 上传到网站根目录: h5ai.inat.top/_h5ai.额,宝塔面板用户,直接就可以远程下载了。打开 http://h5ai.inat.top/_h5ai/public/index.php, 检查一下h5ai 是否可以访问. 同时,如果你的服务器有不兼容的话这里会显示。不过打开的时候,会要求你设置一个密码,当然留空也可以直接访问,登录之后,会有web环境检查的结果,如图:2.4 修改默认首页添加 /_h5ai/public/index.php 到默认的首页列表中添加好之后,我们就可以打开浏览器访问了,界面如下:3.给h5ai开启二维码、搜索、选择功能以下均在配置文件/www.xx.com/_h5ai/private/conf/options.json修改3.1 开启选择功能"select": { "enabled": false, "clickndrag": true, "checkboxes": true },将false修改为true,即:"select": { "enabled": true, "clickndrag": true, "checkboxes": true },该功能可方便我们进行批量下载。3.2开启搜索功能"search": { "enabled": false, "advanced": true, "debounceTime": 300, "ignorecase": true },将false修改为true,即:"search": { "enabled": true, "advanced": true, "debounceTime": 300, "ignorecase": true },开启后我们可以更快的找到文件了。3.3 开启二维码功能"info": { "enabled": false, "show": false, "qrcode": true, "qrFill": "#999", "qrBack": "#fff" },将false修改为true,即:"info": { "enabled": true, "show": true, "qrcode": true, "qrFill": "#999", "qrBack": "#fff" },开启二维码后,我们可以直接手机扫描二维码下载,很方便。参考资料h5ai-轻便又美观目录列表程序支持在线预览文本、图片、音频、视频等给h5ai开启二维码、搜索、选择功能
2021年12月22日
838 阅读
0 评论
0 点赞
2021-12-22
instantbox :几秒内启动一个干净的 Linux 系统
1.介绍instantbox一款非常实用的项目,它能够让你在几秒内启动一个主流的 Linux 系统,随起随用,支持 Ubuntu, CentOS, Arch Linux, Debian, Fedora 和 Alpine,通过 WebShell 访问,简单快捷,适合于演示、测试、体验等场合。喜欢尝试新玩意的青小蛙常常需要一台新的 Linux 系统进行测试,一则不能不影响现有系统,弄坏无压力,二则为了避免原环境对测试的影响。虽然有虚拟机、VPS,但新的 Linux系统还是需要一段时间才能创建完成,尤其如果 VPS 的话,你可能还需要支付几个小时的使用费用,而碰到 IP 不可用那种问题,纯属浪费时间。instantbox 提供了一个非常便捷的解决方案,它能够帮你在几秒钟内就启动一个干净的 Linux 发行版本,并且拥有多个版本,包括:Ubuntu:14.04、16.04、18.04、20.04 版本CentOS:6.10、7、8 版本Arch Linux:最新Debian:jessie、stretch、busterFedora:30、31Alpine:最新可以将 instantbox 理解为虚拟机中的临时虚拟机。2.如何安装 instantboxinstantbox 推荐使用 Docker 部署,直接使用官方提供的脚本即可:mkdir instantbox && cd $_ bash <(curl -sSL https://raw.githubusercontent.com/instantbox/instantbox/master/init.sh)或者下载 docker-compose.yml 文件,直接 docker-compose up -d 即可。3.如何使用 instantbox记得修改里面的端口,默认 8888。然后用浏览器打开 IP:8888,选择系统:选择配置,可选 CPU 核心数、内存、有效期然后,就会启动一个 WebShell 了,请尽情折腾,反正一天后就没了…注意由于其系统十分纯净,很多基础工具也可能需要安装才能使用,以 Ubuntu 为例,比如你要使用 ping,要先安装 apt install iputils-ping 才行。最后,关于权限,任何人知道你的 IP:端口 就能访问并使用,虽然不会对母机造成伤害,但可能会导致资源滥用,请注意保护。参考资料instantbox – 几秒内启动一个干净的 Linux 系统
2021年12月22日
724 阅读
0 评论
0 点赞
1
...
13
14
15
...
24