CNN模型之AlexNet

Alex 2012使用了一些改良CNN方法去解决普适物体识别难题。
开创性地使用了CUDA来加速神经网络训练,并且开放了Cuda-Convnet和绝秘CNN结构,群众反响热烈。又名ConvNet、AlexNet。

模型结构

由于早期GPU显存的限制,最早的AlexNet包括了two stream的设计,以让网络中一半的节点能存入一个GPU。

5层卷积层详情如下:(max pooling: kernel_size=3, stride=2)

层数 input size kernel size # kernels stride padding output size max pooling
1 224×224×3 11×11 96 4 2 55×55×96 yes
2 27×27×96 5×5 256 1 2 27×27×256 yes
3 13×13×256 3×3 384 1 1 13×13×384 no
4 13×13×384 3×3 384 1 1 13×13×384 no
5 13×13×384 3×3 256 1 1 13×13×256 yes

AlexNet的pytorch实现

https://github.com/pytorch/vision/blob/master/torchvision/models/alexnet.py

1
2


为什么跟 https://zhuanlan.zhihu.com/p/31717727 不同

多GPU训练

由于早期GPU显存的限制,AlexNet使用了双数据流的设计,以让网络中一半的节点能存入一个GPU。这两个数据流,也就是说两个GPU只在一部分层进行通信,这样达到限制GPU同步时的额外开销的效果。有幸的是,GPU在过去几年得到了长足的发展,除了一些特殊的结构外,我们也就不再需要这样的特别设计了。

Overlapping Pooling

重叠的池化层

池化操作提取的是一小部分的代表性特征,减少冗余信息。传统的卷积层中,相邻的池化单元是不重叠的。如果stride=kernel_size,我们得到不重叠的池化。如果stride<kernel_size,我们得到重叠的池化。在AlexNet中使用了最大池,stride=2,kernel_size=3。

论文指出,这种池化能“稍微”减轻过拟合。

ReLU

见激活函数-ReLU

学习

使用基于动量的梯度下降算法

batch_size = 128

momentum=0.9

weight_decay=0.0005

code

https://code.google.com/p/cuda-convnet/

tensorflow中的tutorial/cnn也是AlexNet的实现

参考

rsync 原理

简介

rsync是Unix下的一款应用软件,它能同步更新两处计算机的文件与目录,并适当利用差分编码以减少数据传输量。rsync中的一项同类软件不常见的重要特性是每个目标的镜像只需发送一次。rsync可以拷贝/显示目录内容,以及拷贝文件,并可选压缩以及递归拷贝。

在常驻模式(daemon mode)下,rsync默认监听TCP端口873,以原生rsync传输协议或者通过远程shell如RSH或者SSH提供文件。SSH模式下,rsync客户端运行程序必须同时在本地和远程机器上安装。

rsync使用所谓的“rsync算法”来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。

需要解决的问题

  1. 如何判断文件是否变更?
  2. 如何找到变更的部分?
  3. 对于二进制文件怎样处理?
  4. 对于大文件怎样处理?

rsync算法

  1. 按固定大小将A分为多块,每块都计算出一个32位的滚动哈希值和一个128位的MD4(有些也用MD5),发给B一端。
  2. B一端从位置0开始按的同样块大小的滚动哈希值,查找看是否命中A给的某个滚动哈希值,若匹配,则表明B文件中的这块内容与对应的A中的那块内容很可能是一致的,但由于32位的哈希值强度不够,因此再计算MD4,若还是匹配,则确认是一致内容,这时B发给A端匹配的段号。对于那些不能匹配的内容,则发给A端原始内容。
  3. A端得到B端给的匹配信息,构造一个与B一致的复本,若是匹配的块,则拷贝原A文件中对应的块,若是不匹配内容则追加之。

分块Checksum算法

首先,我们会把fileDst的文件平均切分成若干个小块,比如每块512个字节(最后一块会小于这个数),然后对每块计算两个checksum,

  • 一个叫rolling checksum,是弱checksum,32位的checksum,其使用的是Mark Adler发明的adler-32算法,
  • 另一个是强checksum,128位的,以前用md4,现在用md5 hash算法。

为什么要这样?因为若干年前的硬件上跑md4的算法太慢了,所以,我们需要一个快算法来鉴别文件块的不同,但是弱的adler32算法碰撞概率太高了,所以我们还要引入强的checksum算法以保证两文件块是相同的。也就是说,弱的checksum是用来区别不同,而强的是用来确认相同。(checksum的具体公式可以参看这篇文章)

传输算法

同步目标端会把fileDst的一个checksum列表传给同步源,这个列表里包括了三个东西,rolling checksum(32bits),md5 checksume(128bits),文件块编号。

我估计你猜到了同步源机器拿到了这个列表后,会对fileSrc做同样的checksum,然后和fileDst的checksum做对比,这样就知道哪些文件块改变了。

但是,聪明的你一定会有以下两个疑问:

如果我fileSrc这边在文件中间加了一个字符,这样后面的文件块都会位移一个字符,这样就完全和fileDst这边的不一样了,但理论上来说,我应该只需要传一个字符就好了。这个怎么解决?
如果这个checksum列表特别长,而我的两边的相同的文件块可能并不是一样的顺序,那就需要查找,线性的查找起来应该特别慢吧。这个怎么解决?
很好,让我们来看一下同步源端的算法。

checksum查找算法

比对算法

源码

基于rsync的应用

  • Rclone
  • Back In Time

都没听过

基于rsync的改进算法

基于rsync的改进算法主要有多轮rsync和本地rsync两个。

疑问

参考

数组

概述

数组 VS 列表

优缺点

优点

  • 代码优化:它使代码优化,可以轻松地检索或排序数据。
  • 随机访问:可以获取任何位于任何索引位置的数据。

缺点

  • 大小限制:只能在数组中存储固定大小的元素。 它在运行时不会增长其大小。 为了解决这个问题,在Java中使用了集合框架。

不同语言的实现

C

C++

Java

Java数组在内存中如何储存的

Java的数组是存在函数栈中?还是存堆中?为什么这个图里,像是存在堆中?

数组引用变量是存放在栈内存(stack)中,数组元素本质是一个对象,是存放在堆内存(heap)中。通过栈内存中的指针指向对应元素的在堆内存中的位置来实现访问。

基本数据类型的数组在内存分配情况

从图中可看出数组元素直接存放在堆内存中,当操作数组元素时,实际上是操作基本类型的变量。

引用类型数组在内存中如何储存

元素为引用类型的数组,在内存中的存储与基本类型完全不一样。

此时数组元素存放引用,指向另一块内存,在其中存放有效的数据。如图:

在数组参数传递的时候,数组是作为引用参数,传递的是数组的引用指针,将数组引用传递给另一数组引用,但仍然不能改变数组长度(仅仅只是调整数组引用指针的指向)。

疑问

  1. Java在堆中分配数组,能否保证连续性?是等价于C++中的malloc分配数组吗?
  2. Java分配数组的源码在哪?

python

对比

C的数组存储在stack中,Java的数组是对象,存储在堆中。

参考

【Hexo插件系列】日志的自动分类插件 hexo-auto-category

简介

Hexo写日志,通常我们都需要维护一个front-matter信息,包括titledate。博客多了,为了方便日志分类,一般还需要设置categories
比如下面的例子:

1
2
3
4
5
6
7
8
---
title: Hexo简介
date: 2008-08-08
categories:
- web开发
- 前端
- 博客框架
---

久而久之,就会发现很多问题:

  • 工作繁琐:大量的category是重复性工作
  • 容易出错:大小写和中英文目录有可能混杂。比如有个web目录,偶尔我们写成了Web,造成了目录树中冗余的节点。
  • 可维护性差:如果要更改目录树中的节点,就要手动更改每个日志的categories变量。

本文介绍一种自动生成categories的插件 hexo-auto-category官方地址

自动生成 categories

最常用的文件管理策略,就是利用文件系统目录结构(树形结构 directory-tree)。
同样,为了便于管理大量的日志文件,采用目录结构是一种简便可行的方案。hexo-auto-category根据日志文件(Markdown)所在文件目录自动分类,即自动生成markdown的front-matter中的categories变量。

示例

对于博客 source/_post/web/framework/hexo.md,该插件会自动生成以下categories

1
2
3
categories:
- web
- framework

安装

1
$ npm install hexo-auto-category --save

配置

在站点根目录下的_config.yml添加:

1
2
3
4
5
6
# Generate categories from directory-tree
# Dependencies: https://github.com/xu-song/hexo-auto-category
# depth: the depth of directory-tree you want to generate, should > 0
auto_category:
enable: true
depth:

编译 & 部署

1
$ hexo clean && hexo g && hexo d

高级配置

如果只想生成第一级目录分类,可以设置depth属性,比如:

1
2
3
auto_category:
enable: true
depth: 1

如有任何疑问,可在Github Issue提出

关于Monkey Patch猴子补丁

简介

定义

以下是维基百科对猴子补丁的定义

The term monkey patch refers to dynamic modifications of a class or module at runtime, motivated by the intent to patch existing third-party code as a workaround to a bug or feature which does not act as desired.

所谓的猴子补丁,是指在运行时修改类或模块,而不去改变源码,达到hot patch的目的。

猴补丁(英语:Monkey patch)是一种很脏的编程技巧,用拼凑代码的方法修改程序逻辑。

Monkey patching 只能在动态语言中实现。比如Python类的方法其实也只是一个属性,方便运行时修改,所以用Python做猴子补丁非常方便。

Changing a method at runtime instead of updating the object definition is one example。

名字来源

  1. 这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。
  2. 还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。

示例/应用场景

维基百科总结了4种应用场景

  • Replace methods / attributes / functions at runtime, e.g. to stub out a function during testing;
  • Modify/extend behaviour of a third-party product without maintaining a private copy of the source code;
  • Apply a patch at runtime to the objects in memory, instead of the source code on disk;
  • Distribute security or behavioural fixes that live alongside the original source code (an example of this would be distributing the fix as a plugin for the Ruby on Rails platform).

简单示例

对属性 打补丁

以下来自wikpedia示例。
利用猴子补丁,动态修改math标准库中Pi的默认值。(这里仅修改了attributes,也可以对某些method进行重写)

1
2
3
4
5
6
7
8
9
10
>>> import math
>>> math.pi
3.141592653589793
>>> math.pi = 3 # 给标准库打补丁,即运行时修改math的pi属性
>>> math.pi
3
>>> ================================ RESTART ================================
>>> import math
>>> math.pi
3.141592653589793

对方法 打补丁

1
2
3
4
5
6
7
8
9
10
class Foo(object):
def bar(self):
print 'Foo.bar'

def bar(self): # 这是补丁
print 'Modified bar'

Foo().bar()
Foo.bar = bar # 给Foo的bar方法打补丁,即运行时修改类的方法
Foo().bar()

由于Python中的名字空间是开放,通过dict来实现,所以很容易就可以达到patch的目的。

实际应用案例

socket的热补丁

用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉.这样我们在后面使用socket的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了.

SQL注入攻击

网页和数据库

Zope、Plone中的安全补丁

In Zope and Plone, security patches are often delivered using dynamic class modification, but they are called hot fixes.
– wikipedia

很多安全补丁也是一种猴子补丁,只不过叫法不同而已。

Eventlet Patcher

现在我们先来看一下eventlet中的Patcher的调用代码吧,这段代码对标准的ftplib做monkey patch,将eventlet的GreenSocket替换标准的socket。

1
2
3
4
5
6
from eventlet import patcher  
# *NOTE: there might be some funny business with the "SOCKS" module
# if it even still exists
from eventlet.green import socket
patcher.inject('ftplib', globals(), ('socket', socket))
del patcher

Eventlet中大量使用了该技巧,以替换标准库中的组件,比如socket。

未完待续,参考 https://blog.csdn.net/seizef/article/details/5732657

从Gevent学习猴子补丁的设计

异步协程工具Gevent是python上面最有名也支持面最广通用性最好的协程工具,它底层基于greenlet,而且可以通过使用猴子补丁将标准库中的同步模块自动的转换成异步.同时他也提供了方便的并发模型和常用的web服务器工具.

gevent能够 修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> import socket
>>> print(socket.socket) # monkey patch前
<class 'socket._socketobject'>
>>>
>>> from gevent import monkey # monkey patch
>>> monkey.patch_socket()
>>> print(socket.socket)
<class 'gevent._socket2.socket'> # 改变了标准socket

>>> import select # monkey patch
>>> print(select.select) # select()轮询的阻塞调用
<built-in function select>
>>>
>>> monkey.patch_select() # monkey patch
>>> print(select.select) # select()轮询的异步调用
<function select at 0x7fb8a7239d70>

例如,Redis的python绑定一般使用常规的tcp socket来与redis-server实例通信。 通过简单地调用gevent.monkey.patch_all(),可以使得redis的绑定协作式的调度 请求,与gevent栈的其它部分一起工作。

这让我们可以将一般不能与gevent共同工作的库结合起来,而不用写哪怕一行代码。 虽然猴子补丁仍然是邪恶的(evil),但在这种情况下它是“有用的邪恶(useful evil)”。

patch_all

除了socket外,gevent还可以为其他的模块打补丁,一起打补丁可以使用

1
patch_all(socket=True, dns=True, time=True, select=True, thread=True, os=True, ssl=True, httplib=False,subprocess=True, sys=False, aggressive=True, Event=False, builtins=True, signal=True)

函数。

我们可以看到像socket,dns,time,selectthread,os, ssl, httplib,subprocess, sys, aggressive, Event, builtins, signal模块都可以打上补丁,打上以后,他们就是非阻塞的了.

核心协程模块greenlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import gevent

def foo():
print('Running in foo')
gevent.sleep(0) # 这行的作用是什么?
print('Explicit context switch to foo again')

def bar():
print('Explicit context to bar')
gevent.sleep(0) #
print('Implicit context switch back to bar')

gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
])

输出

1
2
3
4
5
Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar
[<Greenlet at 0x7fb8a72c3eb0>, <Greenlet at 0x7fb8a72c3a50>]

上述例子能看到,执行顺序是 foo-->bar--foo--bar,来回切换。即gevent.sleep()并不会真正的阻塞整个线程,而是将cpu的控制权显式的交给未被gevent.sleep()阻塞的协程使用。

协程是单线程程序(从上述例子来讲),如果我们使用time.sleep(),那么整个线程都会被阻塞。

gevent.sleeptime.sleep的区别

  • gevent is a cooperative analog to the threading module. When using gevent.sleep it you would never use time.sleep. So no example is needed.

  • time.sleep would suspend the entire process, blocking all greenlet threads. 来源。 以上说法针对的是协程(单线程程序)。而对于多线程,time.sleep仅仅阻塞当前线程,不阻塞其他线程,来源

猴子补丁 与 SocketIO

用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉.这样我们在后面使用socket的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了.

我看到猴子补丁,是从Gevent中看到的。SocketIO服务器发送数据,浏览器端并非实时接收,而是批量接收 (跟过马路有点像,凑够一波发送一次)。

这里涉及到buffer和flush。

没看懂的部分,后面再看。

That is really the only way to make this work when you use gevent, threading is cooperative so you have to release the CPU so that other tasks associated with the server get a chance to run and flush the messages. Any chance you haven’t monkey patched the standard library?
— Flask-SocketIO的作者miguelgrinberg link

这里说的意思是,socketio.emit(message) 默认会加缓存(buffer)。需要主动flush才能立即发送。而gevent.sleep(是flush的一种方式,因为它会 将cpu的控制权显式的交给未被gevent.sleep()阻塞的协程使用,切换之前会先flush一下。

socketio.emit默认会有个buffer(为了高效),为什么gevent.sleep会flush这个buffer?让我们重新梳理一下思路:

  1. gevent.sleep释放cpu控制权,即切换协程,从而不阻塞其他协程运行。 gevent切换协程的源码
  2. gevent进行协程切换前,需要flush当前协程gevent进行flush的源码
  3. flush当前协程导致socket.emit中的缓存立即发送

猴子补丁与 import json,

之前做的一个游戏服务器,很多地方用的import json,后来发现ujson比自带json快了N倍,于是问题来了,难道几十个文件要一个个把import json改成import ujson as json吗?
其实只需要在进程startup的地方monkey patch就行了.是影响整个进程空间的.
同一进程空间中一个module只会被运行一次.

习题

猴子补丁是动态语言的专利么?

使用猴子补丁的条件主要是可以打开类、可以重定义现有属性、方法。修改类方法的指针,或者属性

C

C++

C++ 类有哪个方法是编译时确定好的, 没法打开类, 对象属于哪个类是 new 对象的代码确定好的, 既然 new 的代码在编译时确定了, 再载入补丁库也修改不了 (除非搞缓冲区溢出攻击…)

比如python中可以math.Pi=3。

java

java强大的反射,即使属性方法被设置为了private final也可以动态更改。讲道理也可以动态补丁。

总结

不是脚本语言的专利……是语言设计留不留口的问题?

猴子补丁的坑

参考

【Angular系列】 Angular 2 入门

runoob教程链接

首先是不掺杂typescript,更能熟悉angular其工作原理。

运行

  1. 下载源码
  2. npm install
  3. 双击html即可。无须借助node server,更容易理解前端框架

执行顺序

  1. npm start启动lite-server,默认加载index.html。或直接双击index.html
  2. 浏览器加载index.html
  3. 浏览器依次加载并执行 app.component.js、app.module.js、main.js
    1. 执行app.component.js,将app.component匿名函数加入到window.app中。并未调用constructor
    2. 执行app.module.js,将app.module匿名函数加入到window.app中
    3. 执行main.js,
  4. main.js调用app.AppModule,即调用app.module匿名函数。其中会调用constructor
  5. app.module调用app.AppComponent,即调用app.component匿名函数,返回template

细节

index.html

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
<html>

<head>
<meta charset="utf-8">
<title>Angular 2 实例 - 菜鸟教程(runoob.com)</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">

<!-- 1. 载入库 -->
<!-- IE 需要 polyfill -->
<script src="node_modules/core-js/client/shim.min.js"></script>

<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>

<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/@angular/core/bundles/core.umd.js"></script>
<script src="node_modules/@angular/common/bundles/common.umd.js"></script>
<script src="node_modules/@angular/compiler/bundles/compiler.umd.js"></script>
<script src="node_modules/@angular/platform-browser/bundles/platform-browser.umd.js"></script>
<script src="node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js"></script>

<!-- 2. 载入 'modules'
顺序不能乱,因为main.js依赖app.module.js,app.module.js依赖app.component.js -->
<script src='app/app.component.js'></script>
<script src='app/app.module.js'></script>
<script src='app/main.js'></script>

</head>

<!-- 3. 显示应用 -->
<body>
<my-app>Loading...</my-app>
</body>

</html>

main.js

定义了一个匿名函数,参数为app

第二个括号用于调用该匿名函数,并传入参数。

1
2
3
4
5
6
7
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
ng.platformBrowserDynamic
.platformBrowserDynamic()
.bootstrapModule(app.AppModule); // 依赖AppModule类
});
})(window.app || (window.app = {}));

app.module.js

定义了一个匿名函数,参数为app

1
2
3
4
5
6
7
8
9
10
11
(function(app) {
app.AppModule = // // 创建一个Angular Module对象,并赋值给对app
ng.core.NgModule({
imports: [ ng.platformBrowser.BrowserModule ],
declarations: [ app.AppComponent ],
bootstrap: [ app.AppComponent ]
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));

app.component.js

定义了一个匿名函数,参数为app

1
2
3
4
5
6
7
8
9
10
(function(app) {
app.AppComponent = // 创建一个Angular Component对象
ng.core.Component({
selector: 'my-app',
template: '<h1>我的第一个 Angular 应用</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));

package.json

用于npm install的依赖和npm start的脚本。

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
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
},
"license": "ISC",
"dependencies": {
"@angular/common": "2.0.0",
"@angular/compiler": "2.0.0",
"@angular/core": "2.0.0",
"@angular/forms": "2.0.0",
"@angular/http": "2.0.0",
"@angular/platform-browser": "2.0.0",
"@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0",
"@angular/upgrade": "2.0.0",

"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"zone.js": "^0.6.23",

"angular2-in-memory-web-api": "0.0.20",
"bootstrap": "^3.3.6"
},
"devDependencies": {
"concurrently": "^2.0.0",
"lite-server": "^2.2.0"
}
}

单页应用

单页应用(英语:single-page application,缩写SPA)是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,而非传统的从服务器重新加载整个新页面。这种方法避免了页面之间切换打断用户体验,使应用程序更像一个桌面应用程序。在单页应用中,所有必要的代码(HTML、JavaScript和CSS)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源并添加到页面。尽管可以用位置散列或HTML5历史API来提供应用程序中单独逻辑页面的感知和导航能力,但页面在过程中的任何时间点都不会重新加载,也不会将控制转移到其他页面。[2]与单页应用的交互通常涉及到与网页服务器后端的动态通信。
– wikipedia

就是基于ajax技术交互的动态页面呗。

诸如AngularJS、Ember.js、Meteor.js、ExtJS和React等面向网页浏览器的JavaScript框架采纳了单页应用(SPA)原则。

这句话怎么理解?这些框架是为了单页应用设计的?为什么这么说?

汇总

参考自掘金 https://juejin.im/post/5a0ea4ec6fb9a0450407725c

单页面应用(SPA) 多页面应用(MPA)
组成 一个外壳页面和多个页面片段组成 多个完整页面构成
资源共用(css,js) 共用,只需在外壳部分加载 不共用,每个页面都需要加载
刷新方式 页面局部刷新或更改 整页刷新
url 模式 a.com/#/pageone a.com/pageone.html
用户体验 页面片段间的切换快,用户体验良好 页面切换加载缓慢,流畅度不够,用户体验比较差
转场动画 容易实现 无法实现
数据传递 容易 依赖 url传参、或者cookie 、localStorage等
搜索引擎优化(SEO) 需要单独方案、实现较为困难、不利于SEO检索 可利用服务器端渲染(SSR)优化 实现方法简易
试用范围 高要求的体验度、追求界面流畅的应用 适用于追求高度支持搜索引擎的应用
开发成本 较高,常需借助专业的框架 较低 ,但页面重复代码多
维护成本 相对容易 相对复杂

疑问

为什么SPA不利于SEO?
单页页面,数据在前端渲染。爬虫一般只抓取静态页面,不会调用js来动态生成新页面。

为什么SPA要首次加载大量资源?
前端渲染,肯定前端需要很多js。SPA一定要前端渲染吗,后端可以渲染好html部分元素返回前台吧?

SPA首次加载大量的静态资源,是前端渲染任务重造成的?

加载顺序

首屏渲染速度除了受js文件大小的影响,还有HTML的解析时机。为了提早加载完document,最好将没有用到的其他文件的下载往后推或者异步下载(不要让他阻塞document的加载)。这里给这些js文件添加了 defer属性。

参考

OCR

OCR文字识别用的是什么算法?–知乎

流程

General OCR一般包含:

  1. detection–>找到包含文字的区域(proposal);
    1. 接着利用radon hough变换 等方法 进行文本校正。
    2. 通过投影直方图分割出单行的文本的图片。
  2. classification–>识别区域中的文字。

framework是: CNN + LSTM + CTC。这个framework加上residue network + stn可以把通用的数据集刷的非常高。

detection

先说detection models, 近两年比较热门的object detection model有 faster-rcnn(https://arxiv.org/pdf/1506.01497.pdf) 和 yolo(http://pjreddie.com/media/files/papers/yolo.pdf), 两个模型都是基于CNN给出proposed regions 同时对object region进行分类。 其中yolo比faster-rcnn的速度更快,但是在accuracy上有些损失。

比较著名的是Ian goodfellow在13年提出的multi-digit number classification

另一类比较常用的方法是RNN/LSTM/GRU + CTC,

开源工具&代码

https://github.com/tesseract4java/jtesseract

开源包: tesseract 很赞

最好的模型,竟然是lstm?https://github.com/tesseract-ocr/tessdata_best

文件管理工具 - Everthing原理 之 还没看懂

简介

Everything is an Awesome Utility that Locates Files and Folders Instantly in Windows

Everything仅支持windows系统的NTFS硬盘格式(不支持FAT、FAT32)。Everything默认对文件名、文件大小、日期以及其它某些meta data建索引,可关闭某些字段索引来加速

  1. 建索引很快
    • 数据库文件 Everything.db。 这是什么类型的数据库?自定义的吗?
  2. 搜索超快
    • 怎样建的索引?咋这么快?建了个hash索引?倒排索引?
  3. 实时性好
    • 怎样获取的新文件列表?大量的临时文件要不要索引?

Everything功能如此强大,让人不禁对其工作原理产生强烈的好奇心。
但是,Everthing官方未开源,这对想学习其工作原理的程序员来说是个bad news。官方提供SDK不知能否看出一些原理逻辑。待看

啊哈,有相关开源项目

微软某成员(疑似轮子哥)在codeplex开源了一个类似everything的个人项目everythingSZ。以下介绍EverythingSZ的原理。

原理

Everything搜索文件很快,是利用的NTFS分区的USN功能.

原理:

  • 读取NTFS下的USN日志文件
  • 根据USN继续查询;
  • 根据文件编号继续查询;
  • 创建USN(激活USN状态);
  • NTFS的Change Journal(更改日志)的方法实现监控功能

未采用 FileSystemWatcher 监听文件变化。(everthing不是采用的这个window api)

如何建索引

如何监听文件变化

这属于操作系统 & 文件系统的范畴。

Windows

即利用windows api。

以下几种方式:

  1. FindFirstChangeNotification
    • 无法获取是哪一个文件发生了改变。
  2. ReadDirectoryChangesW
    • 据说变化量大又密集时,丢失通知现象很严重
  3. FileSystemWatcher
    • 貌似是对ReadDirectoryChangesW的封装
  4. NTFS的Change Journal(更改日志)
    • Change Journal是标卷上一个特殊的文件,系统将其隐藏,所以用资源管理器或者CMD Shell都看不到,当文件系统中的文件或者目录发生改变时,就会向日志中追加记录。参考
    • 通过读取和监控USN(后面会讲)而不是扫描文件来构建索引,所以搜索速度飞快

Everything采用了第四种方式,即利用了NTFS系统的Change Journal特性。

Linux

  • inotify 命令
    • 是Linux自带的监控inode变动的函数
    • 文档 man 7 inotify

其它疑问

linux下有没有类似的工具

比linux下的find命令快,比locate命令实时性好。

参考 & 待看

【Angular系列】Angular 快速入门教程 - Hello Angular

简介

Angular 是由谷歌开发与维护一个开发跨平台应用程序的框架,同时适用于手机与桌面。

其模板基于双向UI数据绑定。数据绑定是一种自动方法,在模型改变时更新视图,以及在视图改变时更新模型。

以下代码来自https://github.com/angular/quickstart

流程简介

  1. 浏览器默认请求index.html
  2. index.html调用main.js
  3. main.js调用app.component.js
  4. app.component.js扫描html,发现有my-app标签,将字符串<h1>Hello </h1>动态插入到my-app元素里。
  5. angular的js扫描html,发现了,向后台发送一个动态请求 (好像是在js中的hard code)

实际node后台顺序貌似是反向的。

流程分解

入口 index.html

以下是index.html,其中调用了main.js

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
<!-- src/index.html -->
<html>
<head>
<title>Angular QuickStart</title>
<base href="/"> <!-- 如果想双击运行html的话,需要去掉这行,并加入angular依赖 -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">

<!-- IE 需要 polyfill -->
<script src="node_modules/core-js/client/shim.min.js"></script>

<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>

<!-- 这里是Angular的入口js文件 -->
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>

<body>
<!-- 使用 AppComponent 组件 -->
<my-app>Loading AppComponent content here ...</my-app>
</body>
</html>

main.ts中设置断点,会看到页面是这样子的。

main.ts

main.ts调用 AppModule模块,会创建AppModule类。(由Angular的NgModuleFactory创建)

1
2
3
4
// src/main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

AppModule 模块

AppModule模块的声明如下。其中调用了AppComponent组件,即首先创建AppComponent类。(由Angular的ComponentFactory创建)

1
2
3
4
5
6
7
8
9
10
11
12
// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

// NgModule指令实现数据的双向绑定
@NgModule({
imports: [ BrowserModule ],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

AppComponent 组件

AppComponent 组件会对html中的my-app标签进行渲染,返回template中指定的元素。

以下是AppComponent组件的声明

1
2
3
4
5
6
7
8
9
10
// src/app/app.component.ts
import { Component } from '@angular/core';
// 通过 Component 装饰器和自定义组件类来创建自定义组件
@Component({ // 定义组件的元信息
selector: 'my-app', // 用于定义组件在HTML代码中匹配的标签
template: `<h1>Hello {{name}}</h1>`, // 定义组件内嵌视图。利用 {{}} 插值表达式实现数据绑定。这是单向绑定吧?
// 双向绑定:<input [(ngModel)]="todo.text">
})
// 定义组件类
export class AppComponent { name = 'Angular'; }

其中变量是在浏览器端渲染。

值得注意的是这里的template
Angular模板基于双向UI数据绑定。在模型改变时自动更新视图,以及在视图改变时自动更新模型。其HTML模板在浏览器中编译。
比如这里的<h1>Hello </h1>

工作流程

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
{
"name": "angular-quickstart",
"version": "1.0.0",
"description": "QuickStart package.json from the documentation, supplemented with testing support",
"scripts": {
"build": "tsc -p src/", # 采用的tsc编译器,node自带的ts编译器。也可以采用webpack,
"build:watch": "tsc -p src/ -w",
"build:e2e": "tsc -p e2e/", # end-to-end tests.
"serve": "lite-server -c=bs-config.json", # 轻量级node静态文件服务器,默认会读取当前目录下的bs-config.js或者bs-config.json文件做为配置导入
"serve:e2e": "lite-server -c=bs-config.e2e.json",
"prestart": "npm run build",
"start": "concurrently \"npm run build:watch\" \"npm run serve\"", # runs the compiler and a server at the same tim
"pree2e": "npm run build:e2e",
"e2e": "concurrently \"npm run serve:e2e\" \"npm run protractor\" --kill-others --success first",
"preprotractor": "webdriver-manager update",
"protractor": "protractor protractor.config.js",
"pretest": "npm run build",
"test": "concurrently \"npm run build:watch\" \"karma start karma.conf.js\"",
"pretest:once": "npm run build",
"test:once": "karma start karma.conf.js --single-run",
"lint": "tslint ./src/**/*.ts -t verbose"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@angular/common": "~4.3.4",
"@angular/compiler": "~4.3.4",
"@angular/core": "~4.3.4",
"@angular/forms": "~4.3.4",
"@angular/http": "~4.3.4",
"@angular/platform-browser": "~4.3.4",
"@angular/platform-browser-dynamic": "~4.3.4",
"@angular/router": "~4.3.4",
"angular-in-memory-web-api": "~0.3.0",
"systemjs": "0.19.40",
"core-js": "^2.4.1",
"rxjs": "5.0.1",
"zone.js": "^0.8.4"
},
"devDependencies": {
"concurrently": "^3.2.0",
"lite-server": "^2.2.2",
"typescript": "~2.1.0",
"canonical-path": "0.0.2",
"tslint": "^3.15.1",
"lodash": "^4.16.4",
"jasmine-core": "~2.4.1",
"karma": "^1.3.0",
"karma-chrome-launcher": "^2.0.0",
"karma-cli": "^1.0.1",
"karma-jasmine": "^1.0.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~4.0.14",
"rimraf": "^2.5.4",
"@types/node": "^6.0.46",
"@types/jasmine": "2.5.36"
},
"repository": {}
}

疑问

  1. 这个project,如何不依赖node,直接双击在浏览器运行?
  2. 这里的是绑定的js中写死的静态变量。如何绑定后台的一个动态变量?

其他angular入门教程