Vim 常见问题(StackOverflow 高票问题)

如何退出Vim 编辑器?

:点击 Esc 键,然后 :q 退出编辑器(或 :q! 不保存退出编辑器,或:q! 退出编辑器不保存,或 :x 等同于:wq 退出编辑器并保存))。

 

如何快速缩进多行?

:利用 > 命令, >5> 或者  5>>  缩进五行, >% 缩进一个block(需要将光标放在括号上)。

 

如何在Vim 中符号替换为换行符,:%s/,/\n/g 将会插入^@ 字符?

:用/r 代替 /n 。在Vim 中,/r 和 /n 分别插入0X00 和0X0A 字符。

 

Vim 如何高亮搜索?

set nohlsearch 关闭高亮, set hlsearch! 打开高亮。

 

如何设置搜索大小写不敏感?

: /letter\c搜索letter 且大小写不敏感,/letter\C 大小写敏感(默认);或:set ignorecase 设置大小写不敏感。

 

如何复制一行?

yy 复制一行,dd 剪切一行,p  粘贴内容在本行下一行,P 粘贴内容在本行上一行。

 

w !sudo tee % 如何实现sudo (root 权限)保存的功能?

% 表示当前文件,tee 将即输出到% 还输出到屏幕。

 

如何设置Tab 为4 个空格,并自动缩进?

:在vimrc 中添加

filetype plugin indent on
" show existing tab with 4 spaces width
set tabstop=4
" when indenting with '>', use 4 spaces width
set shiftwidth=4
" On pressing tab, insert 4 spaces
set expandtab

 

如何移动到一行的最后?

A 移动到本行最后并进入插入模式或 $ 移动到最后(普通模式,normal mode)

 

如何编辑多个文件?

:使用tabs(Vim7 中引入),:tabe <filepath> 在新tab 中打开文件,:tabn 和 :tabp 在文件之间切换。还可以通过:sp <filepath> 进行分屏,ctrl+w 在屏幕之前切换。

 

remap/map/noremap 命令

remap 是使得映射命令能够递归地映射。系统默认是开启的(建议)。map/noremapd 都是映射命令。:map 和 :noremap 是递归映射(recursive)和非递归映射(no-recursive)。

:map j gg
:map Q j
:noremap W j

j 映射为 ggQ 递归地映射为 gg。 W 将不会映射到 gg 而只会映射到 j ,因为它使用非递归映射。考虑到还有普通模式(normal)可视模式(visual),还有对应的映射命令(:nmap 和 :nnoremap)与 (:vmap 和 :vnoremap)。

 

如何关闭粘贴时的自动缩进?

:set paste 进入插入粘贴模式(显示-- INSERT (paste) --),粘贴后取消 -:set nopast。可以将<F3> 键进行映射,从而快速切换模式::set pastetoggle=<F3>

 

如何使用寄存器(registers)?

:Vim 中寄存器可以用于暂时存储文本、宏命令等,以备后面使用。

:reg a b c 打印出  a b c 寄存器中内容;"a 表示寄存器a,"ayy 复制一行到寄存器a,"ap复制到当前位置;"Ayy 将内容增添在寄存器a;"+ 是一个特殊寄存器指向系统剪切板(clipboard),"+p 将系统剪切板中的内容粘贴到当前位置; "0 – "9 是九个数字寄存器,"0 寄存器保存复制(yank)的内容,"1 – "9 按时间分别保存删除(dd)的内容。

 

如何快速注释多行?

<Ctrl>+v 进入可视块(visual block)模式,选择多行,<Shift>+i 进入插入(insert)模式,在行首加入注释符(//,#),<Esc> 键将在选择的多行前都添加注释符。

 

如何用字符显示空格?

:set listchars=eol:¬,tab:>·,trail:~,extends:>,precedes:<,space:␣

:set list (根据我的经验,不要显示空格,或者空格用点符号更美观  space:·

 

什么是记录(Vim recording)?

q<letter> 开始记录,再次按q 结束记录,并通过@<letter> 回放。它可以记录所有你输入的,比如查找,移动,替换等操作。非常有用。

 

如何删除空白行?

:g/^$/d

 

如何实现全文缩进对齐?

gg=G。 gg 跳到文件开头, = 缩进对其,G 到文件末尾。

 

如何粘贴内容到系统剪切板(clipboard),或者从系统剪切板到Vim 中?

:寄存器 "* 和 "+ 是系统剪切板寄存器,"*yy 或者 "+yy 将复制行到系统剪切板,"*p 和 "+P 粘贴到本行下一行或者上一行。(我更习惯用<shift>+<insert> 粘贴)

 

如何将(Win)dos 中文件行结束转化为Linux 行结束?

dos2unix 是专门处理这个的工具。或者使用替换命令 :%s/^M//g ,或 :set ff=unix 。

 

如何 “重做”(redo) “回退”(undo)?

u 回退,<Ctrl>+r 重做。

 

如何移动屏幕,而不需要移动光标?

zz 移动当前行到屏幕中间,zt 移动当前行到屏幕顶部,zb 移动当前行到屏幕底部。

 

如何删除行而不存在寄存器中?

"_d 删除到“黑洞”寄存器

 

查找下一个

n 查找文件的上一个,N 查找文件中上一个

 

如何设置非贪婪正则表达?

:用.\{-} 代替.* 。

 

如何设置内容超过80 列后提醒?

:在 ~/.vimrc 内添加

highlight OverLength ctermbg=red ctermfg=white guibg=#592929
match OverLength /\%81v.\+/

 

如何复制文件中所有行?

gg"*yG 复制内容到"* 寄存器。

 

如何关闭单个打开的文件缓存(e.g. vim a.txt b.txt 打开后如何关闭一个文件缓存)?

:bd 关闭当前缓冲区,:ls 列出当前所有缓冲区,:bd2 关闭第二个缓冲区。

 

^M 是什么?

:Unix 用 0xA 表示新行,Windows 则用 0xD 0xA^M 显示为 0xD 。

 

如何关闭所有选项页(tabs)?

:qa 退出所有选项,:wqa保存所有选项页并退出。

 

自动补全?

:YouCompleteMe

 

用遗传算法解决简单的数学等式问题

[翻译]原文:Genetic Algorithm for Solving Simple Mathematical Equality Problem

[翻译]地址:https://arxiv.org/ftp/arxiv/papers/1308/1308.4675.pdf

[翻译]目的:文章旨在为新手解释什么是遗传算法。并使用遗传算法这个工具一步步解决一个具体的数学问题——求解数学等式的解。

基本理念

在通过遗传算法寻找问题解的过程中,用染色体来表示一个解,一堆染色体叫做种群。染色体由基因构成,基因可以是数值的、符号的也可以是其他类型数据结构(视所解决的问题来定)。染色体通过环境适应度来衡量这个解对于问题的优劣程度。种群中的染色体通过交叉(交配)遗传到下一代,子代染色体是父代基因的组合。基因还会发生突变,遗传算法中使用交叉率和突变率来控制。

算法步骤

  1. 决定染色体数目、循环代数(决定遗传多少代)、突变率和交叉率;
  2. 产生染色体种群,并使用随机值初始化染色体中的基因
  3. 重复步骤4-8(重复次数等于循环代数)
  4. 考量每个染色体的适应值
  5. 染色体选取
  6. 交叉
  7. 变异
  8. 产生新的后代染色体
  9. 得到最终解

算术问题

以求解多元一次不等式 a+2b+3c+4d=30 为例,使用遗传算法找到a b c d 值满足该等式。很显然可以使用这样一个函数衡量适应度

f (x) = |(a + 2b + 3c + 4d) - 30|

a b c d 初始化为0到30之间的一个自然数。

步骤一 初始化                            (下面步骤不完全和算法步骤对应)

随机生成六个染色体Chromosome[1-6]。

Chromosome[1] = [a;b;c;d] = [12; 5; 23; 8]

Chromosome[2] = [a;b;c;d] = [ 2; 21; 18; 3]

Chromosome[3] = [a;b;c;d] = [10; 4; 13; 14]

Chromosome[4] = [a;b;c;d] = [20; 1; 10; 6]

Chromosome[5] = [a;b;c;d] = [ 1; 4; 13; 19]

Chromosome[6] = [a;b;c;d] = [20; 5; 17; 1]

步骤二 评估

计算每个染色体的适应度。

F_obj[1] = Abs(( 12 + 2*05 + 3*23 + 4*08 ) - 30) = 93

F_obj[2] = Abs((02 + 2*21 + 3*18 + 4*03) - 30) = 80

F_obj[3] = Abs((10 + 2*04 + 3*13 + 4*14) - 30) = 83

F_obj[4] = Abs((20 + 2*01 + 3*10 + 4*06) - 30) = 46

F_obj[5] = Abs((01 + 2*04 + 3*13 + 4*19) - 30) = 94

F_obj[6] = Abs((20 + 2*05 + 3*17 + 4*01) - 30) = 55

步骤三 选择

适应度是越小越好,我们将适应度取倒数(加1避免出现除以0的错误),并归一化得到一个概率P

Fitness[1] = 1 / (1+F_obj[1]) = 1 / 94 = 0.0106

Fitness[2] = 1 / (1+F_obj[2]) = 1 / 81 = 0.0123

Fitness[3] = 1 / (1+F_obj[3]) = 1 / 84 = 0.0119

Fitness[4] = 1 / (1+F_obj[4]) = 1 / 47 = 0.0213

Fitness[5] = 1 / (1+F_obj[5]) = 1 / 95 = 0.0105

Fitness[6] = 1 / (1+F_obj[6]) = 1 / 56 = 0.0179

总计 Total = 0.0106 + 0.0123 + 0.0119 + 0.0213 + 0.0105 + 0.0179 = 0.0845

P[1] = 0.0106 / 0.0845 = 0.1254

P[2] = 0.0123 / 0.0845 = 0.1456

P[3] = 0.0119 / 0.0845 = 0.1408

P[4] = 0.0213 / 0.0845 = 0.2521

P[5] = 0.0105 / 0.0845 = 0.1243

P[6] = 0.0179 / 0.0845 = 0.2118

从结果可以看出基因4具有最高的适应度。接下来计算累计概率分布CPD:

C[1] = 0.1254

C[2] = 0.1254 + 0.1456 = 0.2710

C[3] = 0.1254 + 0.1456 + 0.1408 = 0.4118

C[4] = 0.1254 + 0.1456 + 0.1408 + 0.2521 = 0.6639

C[5] = 0.1254 + 0.1456 + 0.1408 + 0.2521 + 0.1243 = 0.7882

C[6] = 0.1254 + 0.1456 + 0.1408 + 0.2521 + 0.1243 + 0.2118 = 1.0

%e4%bf%a1%e6%81%af

然后生成六个随机数R[i=1,2,3,4,5,6],通过这六个随机数在上图中的位置区间选择新的染色体,比如,当随机数在0.4118到0.6639之间则选择染色体4。不难看出,这种方法反应了适应度好的染色体具有更大概率被选择。

随机生成的六个随机数如果是:

R[1] = 0.201

R[2] = 0.284

R[3] = 0.099

R[4] = 0.822

R[5] = 0.398

R[6] = 0.501

那么相应的六个新的染色体是:

NewChromosome[1] = Chromosome[2] = [ 2; 21; 18; 3]

NewChromosome[2] = Chromosome[3] = [10; 4; 13; 14]

NewChromosome[3] = Chromosome[1] = [12; 5; 23; 8]

NewChromosome[4] = Chromosome[6] = [20; 5; 17; 1]

NewChromosome[5] = Chromosome[3] = [10; 4; 13; 14]

NewChromosome[6] = Chromosome[4] = [20; 1; 10; 6]

 

步骤四 交叉

假设交叉率为25%(0.25),那么生成6个随机数,如果随机数小于交叉数,则对应的染色体被选择用于交叉。比如随机数为:R[1] = 0.191, R[2] = 0.259, R[3] = 0.760, R[4] = 0.006, R[5] = 0.159,R[6] = 0.340。那么得到用于交叉的三对染色体是NewChromosome[1], NewChromosome[4] 和NewChromosome[5]。交叉方式为:

NewChromosome[1] >< NewChromosome[4]

NewChromosome[4] >< NewChromosome[5]

NewChromosome[5] >< NewChromosome[1]

下面决定从那个位置进行交叉。因为染色体长度为4,则随机生成1-(4-1) 大小的随机数,随机数用于表示基因从哪里开始交叉。

C[1] = 1 C[2] = 1 C[3] = 2

CrossChromosome[1] = [ 2; 21; 18; 3] >< [20; 5; 17; 1] = [ 2; 5; 17; 1]     // C[1] = 1

CrossChromosome[2] = [20; 5; 17; 1] >< [10; 4; 13; 14] = [20; 4; 13; 14]     // C[2] = 1

CrossChromosome[3] = [10; 4; 13; 14] >< [ 2; 21; 18; 3] = [10; 4; 18; 3]    // C[3] = 2

那么剩余的种群中则有染色体:

NewChromosome[2] = [10; 4; 13; 14]

NewChromosome[3] = [12; 5; 23; 8]

NewChromosome[6] = [20; 1; 10; 6]

CrossChromosome[1] = [ 2; 5; 17; 1]

CrossChromosome[2] = [20; 4; 13; 14]

CrossChromosome[3] = [10; 4; 18; 3] 

步骤五 突变

假设突变概率为10%(0.1)。考虑到6个染色体中每个有6个基因,那么将有4×6×0.1= 2.4 ≈ 2个基因发生突变,通过产生两个1-24范围的数值得到突变染色体位置(假设为12和18)。接着还产生两个1-30之间的随机数作为突变后的结果(假设为2 和 5)。那么得到新的种群如下(红色为突变基因):

Chromosome[1] = [10; 4; 13; 14]

Chromosome[2] = [12; 5; 23; 8]

Chromosome[3] = [20; 1; 10; 2]

Chromosome[4] = [ 2; 5; 17; 1]

Chromosome[5] = [20; 5; 13; 14]

Chromosome[6] = [10; 4; 18; 3] 

步骤六 回到步骤二进行迭代

步骤七 至循环代数次结束

步骤六和步骤七类似,不再赘述。

整个过程如下图所示:

如何为程序绑定不同的网络连接出口

问题:个人笔记本连接着手机上网(生成本地连接3),同时连接wifi(生成无线连接2),需要通过wifi远程连接服务器。如何能够正常上网(浏览器,QQ等),也可以通过wifi 远程连接服务器。

%e7%bb%98%e5%9b%be1

解决方法:将本地连接3设置为家庭网络(所有程序优先走该连接),无线连接2设置为工作或者公开网络,并通过ForceBindIP 将xshell 应用绑定到无线连接2对应的IP 地址。

注意:USB连接上手机生成的本地连接3每次在重新插上时IP 都会发生变化,所以不是很好绑定应用于该连接。无线连接2必须设置为固定IP。

STM32F10x 单片机移植Modbus

最近在做STM32F10x 单片机移植Modbus的工作。Modbus是串口通信中广泛使用的协议,为了将其移植到stm32f10x单片机中,我们通过其开源实现freeModbus 进行了协议分析。stm32f10x 是一款被广泛使用的单片机,具有能耗小、价格低、速度快和中断响应迅速等优点。

首先,本文主要对Modbus 协议分析,有少量涉及到stm32单片机的内容;其次,文章主要以从机作为分析对象。(主从机是通信的一种模式,主机用于发送指令或者查询数据,从机用于应答)。另外Modbus 只有从机开源。

文章中参考了一些网友的资料,如有需要,可以参考下面文章:

  1. STM32单片机嵌入式实战教程 (stm32单片机视频讲解)
  2. Freemodbus RTU在stm32上的移植分析    (包含Modbus 发送接收状态机)
  3. freeModbus 主机的改写 (包含主机部分代码)
  4. http://www.freemodbus.org/ (freeModbus 官网)

下面按照几个方面进行协议分析:

一、单片机上串口通信的数据帧

单片机上串口通信通过数据帧完成,但必须规定一帧的开始和结束才能够进行正常的数据读取。简单的方法就是在帧的两端定义特殊的开始字节和结束字节。但这样的话,要求数据部分不能出现与开始字节和结束字节相同的内容,且帧的开始结束都是由应用程序判断。Modbus使用更“硬件”的方法,它通过两个帧之间的时间间隔来判断一个帧,即如果两个字节相隔一段时间(如传输3.5个字符时间),则认为新的的数据是新的一帧。

二、Modbus 启动流程

我们以freeModbus 为例进行分析(从参考[4] 可以下载到freeModbus 的源代码)。源码中mb.c是Modbus 的主要接口函数,include 目录是头文件,tcp/ascii/rtu 是Modbus 支持的三种传输模式,rtu 是最常用的,本文只涉及rtu模式。

主函数main() 中包含三个部分:

eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );
eMBInit() 是初始化函数,使用“模式,端口,地址,波特率,校验”几个参数初始化系统硬件相关信息。

eStatus = eMBEnable(  );
使能函数,使得Modbus 整个协议栈工作起来。具体会做开启一些中断等工作。

for( ;; ){
( void )eMBPoll( );
}

这是从机的主要工作部分,系统经过简单的配置和使能后,就进入eMBpoll()这样的服务模式,类似于Linux 中I/O多路复用的epoll(),当有事件发生时则做出相应响应。

整个Modbus工作流程如下图,也可以看出初始化、使能和eMBPoll()服务三个部分。

untitled-flowchart

初始化:eMBInit()在mb.c中根据定义的是rtu 还是asccii等模式会给接口函数(函数指针)进行初始化,这样的好处是对应于不同的模式使用统一的接口函数。当模式为rtu时:

pvMBFrameStartCur = eMBRTUStart;     \\开始接收数据帧
pvMBFrameStopCur = eMBRTUStop;       \\停止接收数据帧
peMBFrameSendCur = eMBRTUSend;       \\数据帧已经发送
peMBFrameReceiveCur = eMBRTUReceive; \\数据帧接收完毕
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
pxMBFrameCBByteReceived = xMBRTUReceiveFSM;       \\接收到数据帧内一个Byte
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;   \\接收到空
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;    \\发生T35 中断

其中,然后就进入了rtu模式下的初始化eMBRTUInit(),其中主要是对串口中断和时钟中断进行的初始化(xMBPortSerialInit()和xMBPortTimersInit())。串口中断会开启相应的RCC时钟,配置GPIO口,配置串口模式(速率等),这和一般的单片机串口通信初始化类似;时钟中断初始化也是要开启相应的RCC时钟,设置时钟模式。需要指出:

Modbus用3.5个字符传输时间间隔作为隔断数据的判断依据,即从时钟中断使能开始,3.5个字符传输时间发起一次中断,记作T35

但当速率超过19200时,则使用固定的时间作为中断依据,这些都在eMBRTUInit()源码中有注释。

使能:eMBEnable()是Modbus 的使能函数,主要是通过函数pvMBFrameStartCur()(rtu模式下指向eMBRTUStart())进行串口中断和时钟中断使能。使能就是使得相应功能能够正常开始工作了。

基于事件的服务模式:eMBPoll()用于循环询问是否有新来事件,有则处理。

三、基于事件驱动的服务模式

从eMBPoll() 源码可以看出:

eMBpoll() 使得整个系统处于一个事件驱动的工作模式,当有新事件到达则处理它,否则就一直循环询问。

进入mb.c 中的eMBpoll() 函数可以看到,首先使用

xMBPortEventGet( &eEvent )

获取可能的事件信息,可能的事件有:

  • EV_READY:准备事件,可以开始接收数据了;
  • EV_FRAME_RECEIVED:数据帧到达事件,如果是发给自己的数据帧,则发起一个EV_EXECUTE 的事件;
  • EV_EXECUTE:执行事件,根据已定义的功能码和功能函数对数据帧进行相应处理 (eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ););
  • EV_FRAME_SENT:数据帧已经发送状态

那么什么时候系统会产生事件呢:中断和状态改变。当发生中断时,中断处理函数可能修改系统所处状态,并发起事件。系统中可以使用xMBPortEventPost() 发起事件。

四、各种状态

系统设置了几类状态,如eMBErrorCode 类型的eStatus 表示整个系统的总体状态,有下面几种:

  • MB_ENOERR,            /*!< no error. */
  • MB_ENOREG,            /*!< illegal register address. */
  • MB_EINVAL,               /*!< illegal argument. */
  • MB_EPORTERR,        /*!< porting layer error. */
  • MB_ENORES,             /*!< insufficient resources. */
  • MB_EIO,                      /*!< I/O error. */
  • MB_EILLSTATE,          /*!< protocol stack in illegal state. */
  • MB_ETIMEDOUT        /*!< timeout error occurred. */

eMBState 表示Modbus 协议在系统中的状态,有以下几种:

  • STATE_ENABLED
  • STATE_DISABLED
  • STATE_NOT_INITIALIZED

另外还有数据接收流程和数据发送流程中的不同状态,数据接收状态eRcvState有:

  • STATE_RX_INIT,         /*!< Receiver is in initial state. */
  • STATE_RX_IDLE,        /*!< Receiver is in idle state. */
  • STATE_RX_RCV,         /*!< Frame is beeing received. */
  • STATE_RX_ERROR     /*!< If the frame is invalid. */

数据发送状态eMBSndState有:

  • STATE_TX_IDLE,      /*!< Transmitter is in idle state. */
  • STATE_TX_XMIT       /*!< Transmitter is in transfer state. */

接收状态eRcvState和发送状态eMBSndState都在mbrtu.h/c 中定义,并在mbrtu.c中实现了接收/发送过程的状态机(xMBRTUReceiveFSM和xMBRTUTransmitFSM)。

五、两类状态机

xMBRTUReceiveFSM()和xMBRTUTransmitFSM()两个函数在mbrtu.c中实现了接收和发送过程中的状态机,两种状态机的状态变化在参考[2]中有详细描述,这里引用文中的图。

中断时改变发送接收状态的唯一手段,所以我们必须知道有哪些中断会改变发送和接收状态,从两个状态机来看,无非两种:数据到达或者发送的串口中断和T35对应的时钟中断。

六、两类中断

Modbus 在启动时进行了串口中断初始化和时钟中断初始化,两类中断在有字节到达串口和过了T35时间时会发生中断。串口USART1的中断处理函数为USART1_IRQHandler(),时钟TIM2的中断函数为TIM2_IRQHandler()。前者在portserial.c 中,后者在porttimer.c 中。

下图是串口USART1的中断处理流程,流程中可能不完全包括对发送和接收状态的修改,具体需要在源码中查看。

uart-interrupt-and-fsm

当捕捉到USART1串口中断时,首先判断是接收中断还是发送中断。在接收第一个字节后,会将发送空闲状态(STATE_RX_IDLE)变化为接收状态(STATE_RX_RCV),此后只要不溢出接收缓冲区就一直接收,没接收一个字符,无论哪个状态都要调用vMBPortTimerEnable()重置时钟中断。接收数据首先要求发送处于发送空闲状态(STATE_RX_IDLE),然后逐个发送数据。

timer-interrupt

当时钟TIM2中断使能后,每过T35时间系统就会发起TIM2的中断,并由porttimer里的TIM2_IRQHandler() 函数处理,中断处理函数根据数据接收状态(eRcvState)发起不同的事件,如当系统处于初始化状态(STATE_RX_INIT)过了T35时间,表示系统可以进入准备状态(发起准备系统准备EV_READY)接收数据了;如果系统处于数据接收状态,则表示数据数据已经接收完毕(还记得串口中断中如果一直有连续数据进来,则接收一个字节就重新使能时钟中断);vMBPortTimersDisable()最后失效所有时钟中断(没关系当接收状态发生改变时,系统中断会被开启)。

 

以上赘言是个人总结和网络资料的汇编。

 

一个监督的赫布学习(Hebb Learning)的例子

赫布学习(Hebb  Learning)基于赫布规则(Hebb Rule):

When an axon of cell A is near enough to excite a cell B and repeatedly or persistently takes part in firing it, some growth process or metabolic change takes place in one or both cells such that A’s efficiency, as one of cells firing B, is increase.

赫布规则大致说的是如果神经细胞刺激不断加强,两者联系加强。

首先看看一个简单的神经网络的结构(以识别为例):

networkneural

左边P(R×1的向量) 是输入,表示待识别物体的R 个特征。W是权重矩阵,通过计算特征和权重矩阵的乘法,用于形成S 个结果,S是判别函数。最终形成a (S×1向量)的结果。下面以位矩阵的数字识别为例:

问题:有6×5大小的像素矩阵用于表示数字0,1,2,如下图所示pattern012

每个数字矩阵用一个一维的特征向量表示,比如0 对应的特征向量为p1:

p1 = [-1 1 1 1 -1 1,-1 -1 -1 1 1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]^T

其中-1代表这个像素不上色,1反之,t1-t3分表代表结果是0,1,2。那我们的问题是如果识别带有误差,或者只有部分像素的例子。如下面图中应该识别为多少呢?

0inbroknbroken2

 

 

 

 

分析:使用如下的神经网络,

networkpractical

权重矩阵W通过下面等式计算:

W = p1·p1^T + p2·p2^T + p3·p3^T

在我们这个例子里,权重函数如下

weightmatrix

S判别函数我们使用hardlims,当输入大于0则结果为1,当小于0 则结果为-1. 针对一个特定识别过程(如下图):

recog0

下面是实现这个过程的Python 代码,使用到numpy 库。

#_*_coding:utf-8_*_
import os
import sys
import numpy as np
mat0 = np.matrix([-1,1,1,1,-1,\
1,-1,-1,-1,1,\
1,-1,-1,-1,1,\
1,-1,-1,-1,1,\
1,-1,-1,-1,1,\
-1,1,1,1,-1])
mat1 = np.matrix([-1,1,1,-1,-1,\
-1,-1,1,-1,-1,\
-1,-1,1,-1,-1,\
-1,-1,1,-1,-1,\
-1,-1,1,-1,-1,\
-1,-1,1,-1,-1])
mat2 = np.matrix([1,1,1,-1,-1,\
-1,-1,-1,1,-1,\
-1,-1,-1,1,-1,\
-1,1,1,-1,-1,\
-1,1,-1,-1,-1,\
-1,1,1,1,1])
mat0t = mat0.getT()
mat0p = mat0t.dot(mat0)
mat1t = mat1.getT()
mat1p = mat1t.dot(mat1)
mat2t = mat2.getT()
mat2p = mat2t.dot(mat2)
print "===============matrix 0===================="
print(mat0p)
print "===============matrix 1===================="
print(mat1p)
print "===============matrix 2===================="
print(mat2p)
matw = mat0p+mat1p+mat2p
print "===============matrix sum===================="
print matw
testa0 = np.matrix([-1,1,1,1,-1,\
1,-1,-1,-1,1,\
1,-1,-1,-1,1,\
-1,-1,-1,-1,-1,\
-1,-1,-1,-1,-1,\
-1,-1,-1,-1,-1])
mata0 = matw.dot(testa0.getT())
print "=========== raw mata0 =============="
print mata0
for ii in xrange(mata0.size):
if mata0[ii] > 0:
mata0[ii] = 1
else:
mata0[ii] = -1
print "============= After testa0 ================="
print mata0

备注:这是Neural Network Design 的一个例子,作者用python 代码实现了下。