• 这里占个坑
  • 我想些反向搜索,但是我自己都没搞懂

Content Attention

  • 假设cnn之后,feature map是$T\times1$,用$x$表示,这里用caffe的表示方法,宽度在前,高度在后,忽略了batch size

  • Content Attention是最常见的Attention,计算权重只用到了$x$和LSTM的输出$c_{t-1}$,$c_{t-1}$和$x$都需要一个全连接

  • 其实这里的$\hat{e}{t,j}, j=1..T$是一起算的,$wx$的shape已经是$T\times C$,而$wc{t-1}$是$1\times C$,需要扩增到$T\times C$

    $$
    \hat{e}{t,j}=wc{t-1}+wx_j \

e_{t,j}=tanh(\hat{e}{t,j}) \
\alpha
{t,j}=softmax(we_{t,j})
$$

  • $\alpha_t$的shape是$T\times1$,是$x$每个位置的权重,然后进行累加,$g_t$称之为glimpse向量,

    $$
    g_t=\sum_{j=1}^{T}\alpha_{t,j}x_j
    $$

  • 然后用LSTM解码,这里$y_{t-1}+g_t$其实是不能直接加到,shape不一致,需要先对$y_{t-1}$全连接一下

    $$
    c_t,h_t=LSTM(y_{t-1}+g_t, c_{t-1}) \

y_t=argmax(softmax(wh_t))
$$

Hybrid Attention

  • Content有一个严重对问题是计算权重对时候,只用到了内容信息,没有用到位置信息,所以会出现对不齐对问题

  • Hybrid Attention其实是Content Attention和Locate Attention的混合,区别只是体现在$\hat{e}_{t,j}$的计算上多了上一时刻的位置信息

    $$
    \hat{e}{t,j}=wc{t-1}+wx_j+w\alpha_{t-1,j}
    $$

  • $\alpha_{t-1,j}$的shape是$T\times N\times1$,先换成$N\times T\times1$,然后用$1\times1$的卷积,卷成$N\times T\times C$,然后可以全连接,最后再换回$T\times N\times C$,这样就能加起来了

  • 理论上,宏函数是不能重载的,第二个宏会直接覆盖掉第一个
  • 但我今天看到了一个非常sao优雅的方法,可以功能上实现重载,原文在这里
1
2
3
4
5
6
7
8
9
#define OneArgument(a) // ...action with one argument
#define TwoArguments(a, b) // ...action with two arguments

#define GetMacro(_1, _2, NAME, ...) NAME
#define Macro(...) GetMacro(__VA_ARGS__, TwoArguments, OneArgument, ...)(__VA_ARGS__)

// usage:
Macro(a); // OneArument(a) is called
Macro(a, b); // TwoArguments(a, b) is called
  • 其中,__VA_ARGS__是参数...的展开

  • 以此类推,三个参数的重载也是能实现的

1
2
3
4
5
6
7
8
9
10
11
#define OneArgument(a) // ...action with one argument
#define TwoArguments(a, b) // ...action with two arguments
#define ThreeArguments(a, b, c) // ...action with three arguments

#define GetMacro(_1, _2, _3, NAME, ...) NAME
#define Macro(...) GetMacro(__VA_ARGS__, ThreeArguments, TwoArguments, OneArgument, ...)(__VA_ARGS__)

// usage:
Macro(a); // OneArument(a) is called
Macro(a, b); // TwoArguments(a, b) is called
Macro(a, b, c); // ThreeArguments(a, b, c) is called

blob

  • explict,显示构造函数,只对构造函数有用,用来抑制隐式转换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class String {
    explicit String ( int n ); // 本意是预先分配n个字节给字符串,加上explicit,就抑制了String ( int n )的隐式转换,
    String ( const char* p ); // 用C风格的字符串p作为初始化值
    }

    String s2 ( 10 ); //OK 分配10个字节的空字符串
    String s3 = String ( 10 ); //OK 分配10个字节的空字符串

    String s4 = 10; //编译不通过,不允许隐式的转换
    String s5 = 'a'; //编译不通过,不允许隐式的转换

    class A {
    A(int a);
    };
    int Function(A a);
    // 当调用Function(2)的时候,2会隐式转换为A类型

    class A {
    explicit A(int a);
    };
    int Function(A a);
    // 这样,当调用Function(2)的时候,编译器会给出错误信息
  • template

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // suppose I've declared
    template <typename T> void foo(T& t);

    template <> void foo<int>(int& t);
    // declares a specialization of the template, with potentially different body.

    template void foo<int>(int& t);
    // causes an explicit instantiation of the template, but doesn't introduce a specialization.
    // It just forces the instantiation of the template for a specific type.
  • iniline主要是将代码进行复制,扩充,会使代码总量上升,好处就是可以节省调用的开销,能提高执行效率

  • shared_ptr引用计数智能指,可以参考这里

caffe.cpp

  • step1: 命令行下输入./build/tools/caffe train -solver xxx.prototxt 运行了程序的入口caffe.cpp main()

  • step2: caffe.cpp main()根据命令行输入的参数train 调用caffe.cpp train()

  • step3: caffe.cpp train()读取xxx.prototxt的参数 调用solver.cpp Solver()的构造函数创建Solver对象

  • step4: 创建Solver对象的时候需要调用solver.cpp Init()函数来初始化模型的网络

  • step5: solver.cpp Init()函数调用solver.cpp InitTrainNet()和InitTestNets()函数来分别初始化训练和测试网络。

  • step6: InitTrainNet() 通过xxx.prototxt 指定的xxxnet.prototxt读取net的参数,调用net.cpp Net()的构造函数,创建训练网络,

  • step7: net.cpp Net()调用net.cpp Init()函数,通过for循环来1)创建网络中每一个Layer对象,2)设置bottom和top,3)调用layer.cpp Setup(),Setup()里会调用具体layer的LayerSetUp()和Reshape()

  • step8: 调用InitTestNets()创建测试网络,与InitTrainNet()类似

  • step9: 运行返回到caffe.cpp train()中,利用创建好的solver对象调用solver.cpp Solve()函数

  • step10: solver.cpp Solve() 调用 solver.cpp Step()函数,while循环迭代的次数,每次迭代 1)调用net.cpp ForwardBackward()来前向以及后向传播 2)solve.cpp ApplyUpdate()更新参数 3)每一定轮次运行solver.cpp TestAll()

  • caffe.cpp中的main()调用train(),train()中创建solver对象,solver对象初始化会调用solver.cpp中的Init()

  • Init()中,创建InitTrainNet()和InitTestNet()

  • 返回到caffe.cpp的train()中,调用Solver()来训练网络,具体过程在solver.cpp的Step()中实现

  • 以上抄自这里

layer_factory

  • #表示:对应变量字符串化
  • ##表示:把宏参数名与宏定义代码序列中的标识符连接在一起,形成一个新的标识符
  • 连接符#@:它将单字符标记符变换为单字符,即加单引号。例如#define B(x) #@x,则B(a)即’a’,B(1)即’1’
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <cstdio>  
    #define trace(x, format) printf(#x " = %" #format "\n", x)
    #define trace2(i) trace(x##i, d)

    int main(int argc, char* argv[])
    {
    int i = 1;
    char *s = "three";
    float x = 2.0;

    trace(i, d); // i = 1
    trace(x, f); // x = 2.000000
    trace(s, s); // s = three

    int x1 = 1, x2 = 2;
    trace2(1); // x1 = 1
    trace2(2); // x2 = 2

    return 0;
    }
  • 这里吧,我太懒了。

GAN原理

  • GAN的终极目的,其实是用$P_G(x)$拟合真实的$P_{data}(x)$,最直接的想法是用MLE来做,MLE实际上和最小化KL距离是等同的,证明见下张图

  • 机器之心的这篇,基本就是照着李宏毅对课件写的,可以主要看一下为什么说判别器可以衡量两个分布之间的JS散度

  • GAN的过程

  • Goodfellow说上面这种形式的判别器$min \ log(1 − D(G(z)))$不好收敛,就搞成了$max \ log(D(G(z)))$,据李宏毅说,这个修改没啥用,只是Goodfellow偷懒而已。但论文里还是挺有道理的。

In practice, equation 1 may not provide sufficient gradient for G to learn well. Early in learning,
when G is poor, D can reject samples with high confidence because they are clearly different from
the training data. In this case, log(1 − D(G(z))) saturates. Rather than training G to minimize
log(1 − D(G(z))) we can train G to maximize log D(G(z)). This objective function results in the
same fixed point of the dynamics of G and D

conditional GAN

  • 除了图片是否真实外,判别器还要区分生成的图片类别对不对

  • 这种结构的判别器会好一点

  • stack_GAN,GAN直接生成大图的话会比较模糊,所以用两阶段的方法

  • 语音增强和image2image也都可以看作是cGAN


unsupervised conditional GAN

  • 问题:没有监督之后,generator可能会生成很真实,但不满足条件的图片
  • 办法1,直接忽略这个问题,因为如果generator不是太深的话,生成图片和输入图片还是相关的
  • 还有一些加入consistency




  • for循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for file in `ls /etc`

for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done

for (( EXP1; EXP2; EXP3 ))
do
# do something
done

while condition
do
# do something
done

until condition
do
# do something
done

# while : 等效于 while true
# There's no real difference in behavior. Both commands do nothing and exit with a successful status. : emphasizes doing nothing; true emphasizes the successful status.
  • if
1
2
3
4
5
6
7
8
9
10
11
12
if condition
then
# do something
elif
# do something
else
# do something
fi

if [ -e "$filename" ] # 文件是否存在,r、w、x是否可读、写、执行
# d是否为目录、f是否为文件
if [ $var -gt 0 ] # 还有lt、ge、le、eq、ne
  • case
1
2
3
4
case "$varname" in
[a-z]) echo "abc";;
[0-9]) echo "123";; #还不知道为啥这么写,以后再补充吧
esac
  • 文件包含
1
2
source ./function.sh
. ./function.sh
  • 字符串
1
2
3
4
5
string="abcd"
echo ${#string} # 字符串长度

string="zhe pian tai shui le"
echo ${string:13:4} # shui
  • 数组
1
2
3
4
5
arr=(1 2 3 4 5)
echo ${arr[3]}

echo ${#arr[@]} # 数组长度
echo ${arr[#arr[@]-1]} # 最后一个元素
  • 问号,和C里面的问号一样
1
2
a=10
(( t=a<50?0:1 )) # t=0
  • /dev/null是个空文件,清空一个文件可以用cat /dev/null > tmp.log,不想保存log,也不想输出到屏幕,可以1>/dev/null 2>&1
  • 清空一个文件也可用: > tmp.log,:是个内建命令,什么也不做,永远返回0
1
2
:
echo $? # 0
  • cp t.{txt,back} 文件名扩展
  • 大括号和小括号的区别
1
2
3
4
5
a=123
(a=321)
echo $a # 123 在子进程中修改了a的值,对当前进程没影响
{a=321;}
echo $a # 321 想当于是一个匿名函数
  • sudo sh -c "...",引号里的内容都会有sudo权限

  • echo $(( 2#101011 )),这里是2进制的意思

  • trap

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    trap "echo Booh!" SIGINT SIGTERM
    echo "it's going to run until you hit Ctrl+Z"
    echo "hit Ctrl+C to be blown away!"

    while true:
    do
    sleep 60
    done
    # kill pid” 会发送SIGTERM到进程pid.
    # 在终端中敲入interrupt key(DELETE或ctrl+c)会产生SIGINT信号。
    # kill -9 pid” 会发送SIGKILL到进程pid,SIGKILL不能被捕获,会直接结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import requests
from bs4 import BeautifulSoup
import datetime

start_date = datetime.date(2018, 6, 6)
start_rank = 866

cookies = {
'ASP.NET_SessionId': 'obe4o1esws0xutvslbbapc55',
'safedog-flow-item': '',
}

headers = {
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer': 'https://rcgy.zjhui.net/',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh',
}

params = (
('flag', '0'),
('userName', '13122358292'),
('passWord', 'MzAyMDI4MnpqeWQ='),
('md5', '08dcb32a31936c855b5f8f5c21f5b957'),
)

response = requests.get('https://rcgy.zjhui.net/Login.aspx', headers=headers, params=params, cookies=cookies)

headers = {
'Connection': 'keep-alive',
'Pragma': 'no-cache',
'Cache-Control': 'no-cache',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Referer': 'https://rcgy.zjhui.net/System/ApplyRecord.aspx',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh',
}

response = requests.get('https://rcgy.zjhui.net/System/WaitingRecord.aspx', headers=headers, cookies=cookies)

soup = BeautifulSoup(response.content, features="lxml")
rank = soup.find(id='ctl00_ctl00_ctl00_main_main_main_rptPtApplyRecord_ctl00_labPageRank').string
rank = int(rank)

today = datetime.date.today()
remain_days = float(rank) * (today - start_date).days / (start_rank - rank)
remain_days = int(remain_days)
check_in_date = today + datetime.timedelta(days=remain_days)

print "rank:\t", rank
print "days remaining:\t", remain_days
print "check in date:\t", check_in_date

照抄的这里

正则表达式

  • 基础正则,basic regex,即bres
  • 扩展正则,extended regex,即eres
  • perl的正则,perl regex,即pres

不同正则表达式的区别

  • bres需要多写转义
1
2
\{n,m\}, x\|y #bres需要写转义,
{n,m}, x|y #而eres和pres不需要
  • pres可以用下面这些,其它两种不可以。
1
\d, \D, \S, \s

grep

  • 默认的正则为基础正则,”-E”表示eres,”-P”表示pers.
  • egrep等效于grep -E,egrep -P等效于 grep -P

sed

  • 默认是eres,-r表示要用eres,不支持pres
  • mac下到sed和linux还不太一样,写inplace替换要这样,其中,-i后面是
1
sed -i '' 's/http.*ot0uaqt93.bkt.*\//\/images\//g' `ls *.md`

awk

  • 厉害了,只支持eres

uv命令

conda命令

  • 创建虚拟环境
1
conda create -n your_env_name python=x.x anaconda

最后的anaconda可选,有的话,会安装很多包,numpy、sklearn等等

  • 激活
1
source activate your_env_name
  • 安装包
1
conda install -n your_env_name [package]
  • 不激活
1
source deactivate
  • 已经有的虚拟环境
1
conda env list
  • 删除虚拟环境
1
conda remove -n your_env_list -all
  • 检查conda是否安装
1
conda -v
  • 更新conda
1
conda update conda

为什么要用uv

Python 项目管理从传统的 pip 演进到 Conda 或 UV 等现代工具,主要围绕解决环境隔离、依赖冲突、以及依赖关系的可维护性等痛点展开。

以下是基于提供的资料,梳理的 Python 项目管理现代化的演进过程和主要流派:

传统的 pip 安装和环境冲突 (早期的痛点)

Python 在诞生之初,并未考虑工程结构的问题,导致早期的管理相当“放自我”。官方的 pip 规范一直在努力打补丁。

  1. 全局环境与冲突: 在项目初期,使用 pip install flask 等命令会将库安装到一个全局环境中,被计算机上所有 Python 项目共享。
  2. 版本冲突: 这种共享机制带来棘手的问题,例如一个新项目可能需要 Flask 3.11,而另一个旧项目可能只兼容 3.0,导致全局升级后旧项目无法运行。
  3. 依赖地狱: 库通常依赖其他几个库,这些库又有各自的依赖,层层嵌套会引发更多的版本冲突,即所谓的“依赖地狱”。

演进的第一步:环境隔离 (Venv)

为了解决版本和依赖冲突,设计出了虚拟环境(Virtual Environment)。

  1. Venv 的作用: 虚拟环境为每个项目创建了一个独立、干净的 Python 工作空间。
  2. 创建与激活: 可以使用 python -m venv .venv 命令创建虚拟环境。激活环境后,再使用 pip 安装库,这些库就会被安装到该项目的虚拟文件夹中,从而避免冲突。
  3. 背后的原理: 虚拟环境主要通过修改 Python 中的 sys.path 变量来实现。激活环境后,虚拟目录会被添加到这个搜索列表里,确保 Python 在导入模块时能够成功加载安装在虚拟环境路径中的库。

演进的第二步:依赖共享与配置 (requirements.txt 到 pyproject.toml)

解决了环境隔离后,新的问题是如何方便准确地将项目的依赖列表分享给其他人,以便复现环境。

1. requirements.txt (早期主流做法)

  • 做法: 早期最主流的做法是使用 pip freeze 命令,将当前虚拟环境中所有已安装包及其确切版本号输出到一个文件,通常命名为 requirements.txt。接收方只需执行 pip install -r requirements.txt 即可安装依赖。
  • 重大缺陷: pip freeze 无法区分项目真正需要的直接依赖(Direct Dependency)和这些直接依赖引入的间接依赖(Indirect Dependency)。如果项目复杂,情况很快会失控。
  • 孤儿依赖: pip 在卸载包时也无法很好地处理依赖关系。如果卸载了某个包(如 Flask),那些因为 Flask 而被安装的间接依赖仍会留在环境中,成为无人管理的孤儿依赖

2. pyproject.toml (现代标准解决方案)

现代 Python 项目的标准解决方案是使用 pyproject.toml 文件。

  • 统一配置: pyproject.toml 是官方指定的统一配置文件。在此之前,不同的开发工具(如类型检查器 mypy、测试框架 pytest)通常使用各自独立的配置文件,导致根目录下配置零散。如今,绝大多数主流工具都支持了 pyproject.toml
  • 仅声明直接依赖:pyproject.toml 中,开发者只需在 dependencies 列表中声明项目的直接依赖即可。如果将来要删除某个依赖,只需删除配置文件中对应的一行,就不会留下任何孤儿依赖。
  • 安装复杂性: 依赖写好后,可以使用 pip install . 命令来安装。这条命令在背后做了两件事:首先是构建,将当前项目打包成标准 Python 软件包;其次是安装,自动把所有声明的依赖一并安装进来。
  • 开发模式(Editable Mode): 在开发阶段,为了避免源代码被复制到虚拟环境中的 site-packages 目录导致代码修改无法同步,通常需要加上 -e 参数,使用 pip install -e . 进行可编辑安装。

演进的第三步:高级项目管理工具 (UV, Poetry)

纯手工维护 pyproject.toml 的流程存在痛点,例如无法再用简单的 pip install 命令添加新依赖,每次添加都需要手动查找名称和版本号并编辑配置,过程繁琐且容易出错。

  1. 诞生原因: 社区为解决这一痛点,催生了像 UVPoetry 这样的高层项目管理工具。
  2. 高级封装: 这些第三方工具可以被理解为对 venvpip高级封装。它们在底层仍使用 venvpip,但提供了更简单、更统一的接口。
  3. UV 示例: 使用 UV,只需执行 uv add flask 一条命令,就能自动修改 pyproject.toml(添加依赖声明)、自动创建 venv 虚拟环境,并将该包及其所有间接依赖安装到环境中。协作者只需执行 uv sync,即可自动读取配置文件、搭建环境并安装所有依赖。
  4. 运行简化: UV 还提供了 uv run 命令,可以在虚拟环境的上下文中执行命令,无需手动激活环境。

独立流派:Conda 宇宙

与官方 Python 体系并行的是 Conda 宇宙,这是一个从设计之初就考虑周全的跨语言开发平台。

  1. 独立的生态: Conda 最早由 Anaconda 公司提供,现在免费版 MiniConda 等使用更广泛,像 Pixi 也是这个生态的一部分。Conda 体系在底层上与官方 Python 走的不是一条路,它有自己的配置文件、软件仓库,甚至连 Python 解释器都是自己编译的。
  2. 跨语言支持: Conda 不只支持 Python,还支持 Go、Rust、C++、R 等各种语言。因此,它更像一个独立的跨语言开发平台
  3. 设计优势: Conda 从设计之初就将多语言支持、依赖管理和虚拟环境等问题用一套统一的方案解决了。
  4. AI 领域的体现: Conda 的优势在 AI 领域尤为明显,因为 AI 框架的依赖出了名的复杂,经常需要与 NVIDIA 的 CUDA 这种非 Python 库打交道。使用 Conda 安装深度学习框架通常是最省心、最不容易出错的选择
0%