tensorflow学习(八)模型资源

网上有众多基于tensorflow实现的各种网络,这里整理梳理一下

VGG-16

https://github.com/ry/tensorflow-vgg16
这是一个基于caffe转换过来的预训练好的vgg16的模型,参数文件可以通过torrent下载,模型的结构详见vgg16.py,这个网络没有训练的代码,不够nice

ResNet

https://github.com/ry/tensorflow-resnet
这也是一个从caffe转换过来的模型,训练好的模型文件可以通过torrent下载,其模型所需要的模块函数在resnet.py中,其inferenceinference_small函数是完整网络。在train_imagenet.py中将模型声明logits传入resnet_train.py进行训练。

fcn (Fully Convolutional Networks)

https://github.com/MarvinTeichmann/tensorflow-fcn
将caffe的VGG-16模型转换成npy格式后,fcnxx_vgg.py定义了模型,并加载vgg.npy的数据参数。通过test_fcn_vgg.py参数测试图片的分割结果。该网络没有提供训练代码。

YOLO

https://github.com/gliese581gg/YOLO_tensorflow
实现的一个YOLO的图像测试代码,比较简单,使用之前要下载对应的ckpt模型参数文件。没有训练代码,无法重现训练。

Inception-V3

https://github.com/tensorflow/models/tree/master/inception
tensorflow官方提供的一个inception-v3的实现,有较为详细的数据加载,训练,测试的代码。值得仔细研究。

caffe学习(七)caffe-YOLO模型

原本YOLO是一个darknet的独立代码,网上有些大神把其网络用caffe实现了。贴一个传送门https://github.com/xingwangsfu/caffe-yolo。这里参考这个代码实现一下,没有训练代码,需要将darknet训练好的参数转换成caffe格式。

修改deploy

要把yolo_deploy.prototxt里的fc26层中的num_output参数改为实际需要的。

参数模型格式转换

用darknet训练好的网络参数通过create_yolo_caffemodel.py用caffe读进来。darknet的权重保存格式很简单,前4个数是int型,分别表示major、minor、revision和net.seen,第五个参数开始就是一个一维浮点数组,存成二进制文件。读入之后,根据网络结构得到net的各层weights和bias的size大小,并按顺序传入。调用格式如下:

python create_yolo_caffemodel.py -m ./prototxt/yolo_deploy.prototxt -w yolo_final.weights -o yolo.caffemodel

YOLO检测

python yolo_main.py -m ./prototxt/yolo_deploy.prototxt -w yolo.caffemodel -i xxx.jpg

日本攻略

调研

日本旅游局官网http://www.welcome2japan.cn/,介绍的还挺好的,比天朝的官方网站做的高到不知道哪里去了

出国准备注意事项

  • 签证:注意单次签证虽然给15天,建议不要排的太满,10天以内即可。
  • 签证申请:必须通过旅行社,单次签,年收入10万以上,按旅行社的要求即可。签证搞到后,会给一份归国报告书,回国办理登记手续的时候给柜台盖章,寄给旅行社。
  • 免费wifi,下载Japan Connected-Free Wifi的app

景点关键词

  • 京都:寺庙
  • 大阪:环球影城
  • 东京:迪斯尼

现金

按每日每人1万日元准备吧~

购物

超过1万日元消费可以在店内退税,带一张银联卡,便宜,好用

东京攻略

python爬虫入门笔记

写在前面

爬虫其实内容的爬取不是最为困难的地方,真正难点在于如何混在群众之中不被发现。由于我在爬虫界还处于幼儿园水平,所以简单写几个脚本爬点美女图片还勉强,想跟亚一爬谈笑风生就不指望了。

urllib基础

html抓取

import urllib2

request = urllib2.Request("http://www.baidu.com")
response = urllib2.urlopen(request)
data = response.read()

data就得到了百度主页的html信息。

html解析

主要就是用re模块来正则匹配

import re

#返回pattern对象
re.compile(string[,flag])  
#以下为匹配所用函数
re.match(pattern, string[, flags])
re.search(pattern, string[, flags])
re.split(pattern, string[, maxsplit])
re.findall(pattern, string[, flags])
re.finditer(pattern, string[, flags])
re.sub(pattern, repl, string[, count])
re.subn(pattern, repl, string[, count])

比较常用的findall,用列表的形式返回全部能匹配的子串。

反爬伪装

现在主流的反爬策略基本基于三个层面:

  • IP层,同一ip访问过多会被封杀
  • HTTP协议层,判断是否真实的浏览器行为
  • 行为层,判断是否真实用户行为
    上述的程序通常在response.read()的时候会报错10054、10060之类的错误。

伪装浏览器Header

用chrome打开浏览器,调试模式,然后打开Network模块,选择其中一个页面,打开Headers页面,其中最后的agent就是请求的身份,不填写身份的话,很容易被反爬策略封杀掉。

user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36'
headers = {'User-Agent' : user_agent}
#    request = urllib2.Request(url, headers)
request = urllib2.Request(url)
request.add_header('User-Agent', user_agent)
response = urllib2.urlopen(request)
page = response.read()

除此之外,有些反爬策略还有反盗链的设计,需要在headers中传入referer

request.add_header('Referer', 'http://www.taobao.com')

proxy代理设置

如果ip访问次数过多也会被封杀,采用代理服务器的方法来定期更换代理。

enable_proxy = True
proxy_handler = urllib2.ProxyHandler({"http" : 'http://192.168.0.1:8080'})
null_proxy_handler = urllib2.ProxyHandler({})
if enable_proxy:
    opener = urllib2.build_opener(proxy_handler)
else:
    opener = urllib2.build_opener(null_proxy_handler)
urllib2.install_opener(opener)

超时设置Timeout

在urlopen的时候可以通过设置timeout参数解决网站相应过慢造成的影响。

response = urllib2.urlopen('http://www.baidu.com', timeout=10)

发送数据

用来实现和浏览器之间的行为交互

data = urllib.parse.urlencode({"act": "login", "email": "xxxxx@qq.com", "password": "123456"}).encode('utf-8')
request1 = urllib2.Request(url, data=data)           # POST方法
request2 = urllib2.Request(url+"?%s" % data)         # GET方法
response = urllib2.urlopen(request1)

常用超时异常

try:
    urllib2.urlopen(request)
except urllib2.HTTPError as e:
    print(e.code, e.reason)
except urllib2.URLError as e:
    print(e.errno, e.reason)

服务器cookie检查

import http.cookiejar
cookie_jar = http.cookiejar.CookieJar()
cookie_jar_handler = urllib.request.HTTPCookieProcessor(cookiejar=cookie_jar)
opener = urllib2.build_opener(cookie_jar_handler)        # add_handler
response = opener.open(url)

获取cookie

两种方式,1)直接贴到headers中

request = urllib2.Request(url)
request.add_header('Cookie', "PHPSESSID=btqkg9amjrtoeev8coq0m78396; USERINFO=n6nxTHTY%2BJA39z6CpNB4eKN8f0KsYLjAQTwPe%2BhLHLruEbjaeh4ulhWAS5RysUM%2B; ")

2)构建cookie

import http.cookiejar
cookie = http.cookiejar.Cookie(name="xx", value="xx", domain="xx", ...)
cookie_jar = http.cookiejar.CookieJar()
cookie_jar.set_cookie(cookie)
cookie_jar_handler = urllib.request.HTTPCookieProcessor(cookiejar=cookie_jar)
opener = urllib2.build_opener(cookie_jar_handler)
response = opener.open(url)

HTTP身份认证

password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_mgr.add_password(realm=None, uri=url, user='username', passwd='password')
handler = urllib2.HTTPBasicAuthHandler(password_mgr)
opener = urllib2.build_opener(handler)
response = opener.open(url)

简易图片爬虫

关键的就是解析的时候匹配图片内容,得到了图片链接的话,用wget或者其他模块都能保存图片

patternImg = re.compile('<img.*?src="(.*?)"',re.S)
images = re.findall(patternImg, url_data)

参考资料与推荐文章

http://cuiqingcai.com/
数据时代的反爬虫绝技
一个很“水”的Python爬虫入门代码文件
“史上最详细”的Python模拟登录新浪微博流程
微博话题爬取与存储分析

tensorflow学习(七)caffe模型训练结果转换为tensorflow格式

tensorflow官方认定的转换工具为https://github.com/ethereon/caffe-tensorflow。我们尝试一下转换一个caffe训练的图像识别模型

caffe-tensorflow安装

git clone https://github.com/ethereon/caffe-tensorflow

生成模型训练代码

利用convert.py可以将caffe网络定义的deploy.prototxt文件转换为tensorflow的python实现,不过这种转换之后的模型代码还需要import kaffe模块,虽然kaffe.tensorflow.Network没有其他依赖,可以单独加载到新代码中即可使用,方便移植,我还是更倾向于转换之后参考这个代码重新写一份。调用方式如下:

./convert.py ./deploy.prototxt --code-output-path=./myNetTensorflow.py

转换训练好的模型

也可以将caffemodel的weights和biases参数读取出来转换为npy的格式,并通过numpy.load()读取数据。转换数据的脚本如下:

./convert.py ./deploy.prototxt --caffemodel=./mynet.caffemodel --data-output-path=./myNetTensorflow.npy

由于转换过来的npy数据的类型比奇怪,是0-d array,要将其取出来需要如下读取方式

params = np.load()
params = params.item()

注意事项

  • caffe的模型应该是较新的版本,才能转换

  • 并不是所有的caffe模型都能转换为tensorflow,比如caffe的padding支持很多方式,tensorflow目前只支持SAMEVALID

tensorflow学习(六)CNN网络Inception

这次我们来上手一个大型网络,googLeNet。这篇讲一下Inception v3网络的训练和测试以及数据集的加载。

数据准备

将图片按照类别放入不同的文件夹下。格式如下:

data_dir/train/label_0/a.jpg
data_dir/train/label_0/b.jpg
...
data_dir/train/label_1/f.jpg
...
data_dir/validation/label_0/c.jpg
data_dir/validation/label_0/d.jpg
...
data_dir/labels.txt

同时将所有label的类别整理成一个txt放在data_dir/labels.txt文件中,行数代表该类别对应的整数值,从0开始计数,其文本内容如下:

label_0
label_1
label_2
...
label_n

转化成为TFRecord格式

首先通过_find_iamge_files函数得到全量图片的文件名、标签、标签文本信息的对应关系,并打乱排序。然后按线程数将全量文件分块,分别执行_process_image_files_batch。将数据按分块分别写入对应的TFRecordWriter。这个操作的代码我整理保存在了build_image_tfrecord_data.py。其中两个重要的参数,一个是进程数量的控制参数num_threads和Record数据分块的参数num_shards。我这里有个不同,原来代码是读文件之后直接压入Record,我是用OpenCV解码之后,将解码后数据变成string压入Record。代码调用格式如下:

python build_image_tfrecord_data.py \
  --train_directory=mydata/train \
  --validation_directory=mydata/validation \
  --output_directory=mydata/tf_record \
  --labels_file=mydata/labels.txt

网络模型

单机训练

分布式训练

测试评估

参考文献

https://github.com/tensorflow/models/tree/master/inception
http://arxiv.org/abs/1512.00567

tensorflow学习(五)基于CNN的验证码识别

验证码的识别是OCR中的一个重要内容,特别是对于爬虫系统意义重大,传统方法都是单个字符识别,包括二值化处理、字符分割、字符识别的过程。由于现在深度学习的火爆,end-to-end的方法流行起来。这里就用tensorflow实现一个端到端的CNN验证码识别功能。

本文的想法是把验证码看成一个多标签学习的问题,相当于几个有标签的图像识别。这里没有考虑使用LSTM对验证码序列进行学习,因为我个人觉得验证码字符之间的相关性不强,没必要用这种大杀器。

验证码数据集

python-captcha生成验证码数据集,这个库可以生成声音和图像的验证码,安装过程非常简单

pip install captcha

使用示例如下:

from captcha.image import ImageCaptcha

image = ImageCaptcha(fonts=['/path/A.ttf', '/path/B.ttf'])
image.write('1234', 'out.png')

数据输入

验证码图片生成后是通过字节流生成的,可以随机生成,所以训练样本量可以无穷大。我们这里使用定长的4位验证码,包含0~9、a~z、A~Z。图像大小缩放到(30, 80, 3)。代码如下:

import numpy as np
import cv2
import random
import tensorflow as tf
from captcha.image import ImageCaptcha

class OCR_data(object):
    def __init__(self, num, data_dir, batch_size=50, len_code=4, height=30, width=80):
        self.num = num
        self.data_dir = data_dir
        self.batch_size = batch_size
        self.len_code = len_code
        self.height = height
        self.width = width
        self.captcha = ImageCaptcha()
        self.index_in_epoch = 0
        self._imgs = []
        self._labels = []
        for i in range(self.num):
            if i % 100 == 0:
                print '%s images have been created.' % i
            img, label = self.create_captcha()
            self._imgs.append(img)
            self._labels.append(label)
        self._imgs = np.array(self._imgs)
        self._labels = np.array(self._labels)


    def create_captcha(self):
        code, label = self.gen_rand()
        img = self.captcha.generate(code)
        img = np.fromstring(img.getvalue(), dtype='uint8')
        img = cv2.imdecode(img, cv2.IMREAD_COLOR)
        img = cv2.resize(img, (self.width, self.height))
        return (img, label)

    def gen_rand(self):
        buf = ''
        label = []
        for i in range(self.len_code):
            rnd = random.randint(0, 61)
            label.append(rnd)
            if rnd < 10:
                ascii_code = chr(rnd+48)
            elif rnd < 36:
                ascii_code = chr(rnd+65)
            else:
                ascii_code = chr(rnd+97)
            buf += ascii_code
        label_one_hot = self.dense_to_one_hot(label, 62)
        return buf, label_one_hot

    def dense_to_one_hot(self, labels_dense, num_classes):
        num_labels = len(labels_dense)
        index_offest = np.arange(num_labels) * num_classes
        labels_one_hot = np.zeros((num_labels, num_classes))
        labels_one_hot.flat[index_offest + labels_dense] = 1
        labels_one_hot = labels_one_hot.reshape(num_labels*num_classes)
        return labels_one_hot

    def next_batch(self, batch_size):
        start = self.index_in_epoch
        self.index_in_epoch += batch_size
        if self.index_in_epoch > self.num:
            perm = np.arange(self.num)
            np.random.shuffle(perm)
            self._imgs = self._imgs[perm]
            self._labels = self._labels[perm]
            start = 0
            self.index_in_epoch = batch_size
            assert batch_size <= self.num
        end = self.index_in_epoch
        return self._imgs[start:end], self._labels[start:end]

训练

import tensorflow as tf
from captcha_data import OCR_data
# Parameters
learning_rate = 0.001
training_iters = 200000
batch_size = 64
display_step = 20

# Network Parameters
# n_input = 7200  # 30*80*3
n_classes = 62  # 10+26+26

data_train = OCR_data(1000, '/data/captcha_data')
data_test = OCR_data(500, '/data/captcha_data')

# tf Graph input
x = tf.placeholder(tf.float32, [None, 30, 80, 3])
y = tf.placeholder(tf.float32, [None, 4*n_classes])

def print_activations(t):
    print(t.op.name, t.get_shape().as_list())

def weight_variable(shape):
    initial = tf.truncated_normal(shape, dtype=tf.float32, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.0, shape=shape)
    return tf.Variable(initial, trainable=True)

def conv2d(x, W, B, name):
    with tf.name_scope(name) as scope:
        conv = tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
        bias = tf.nn.bias_add(conv, B)
        conv = tf.nn.relu(bias, name=scope)
        return conv

def max_pool(x, k, name):
    return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

def avg_pool(x, k, name):
    return tf.nn.avg_pool(x, ksize=[1, k, k, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

def norm(x, lsize, name):
    return tf.nn.lrn(x, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name)

weights = {
    'wc1': weight_variable([5, 5, 3, 32]),
    'wc2': weight_variable([5, 5, 32, 32]),
    'wc3': weight_variable([3, 3, 32, 32]),
    'wd1': weight_variable([4*10*32, 512]),
    'out1': weight_variable([512, n_classes]),
    'out2': weight_variable([512, n_classes]),
    'out3': weight_variable([512, n_classes]),
    'out4': weight_variable([512, n_classes])
}
biases = {
    'bc1': bias_variable([32]),
    'bc2': bias_variable([32]),
    'bc3': bias_variable([32]),
    'bd1': bias_variable([512]),
    'out1': bias_variable([n_classes]),
    'out2': bias_variable([n_classes]),
    'out3': bias_variable([n_classes]),
    'out4': bias_variable([n_classes]),
}

def ocr_net(_x, _weights, _biases):
    _x = tf.reshape(_x, shape=[-1, 30, 80, 3])

    conv1 = conv2d(_x, _weights['wc1'], _biases['bc1'], 'conv1')
    print_activations(conv1)
    pool1 = max_pool(conv1, k=2, name='pool1')
    print_activations(pool1)

    conv2 = conv2d(pool1, _weights['wc2'], _biases['bc2'], 'conv2')
    print_activations(conv2)
    pool2 = avg_pool(conv2, k=2, name='pool2')
    print_activations(pool2)

    conv3 = conv2d(pool2, _weights['wc3'], _biases['bc3'], 'conv3')
    print_activations(conv3)
    pool3 = avg_pool(conv3, k=2, name='pool3')
    print_activations(pool3)

    pool3_flat = tf.reshape(pool3, [-1, _weights['wd1'].get_shape().as_list()[0]])
    fc1 = tf.nn.relu(tf.matmul(pool3_flat, _weights['wd1']) + _biases['bd1'], name='fc1')
    print_activations(fc1)

    fc21 = tf.nn.relu(tf.matmul(fc1, _weights['out1']) + _biases['out1'], name='fc21')
    print_activations(fc21)

    fc22 = tf.nn.relu(tf.matmul(fc1, _weights['out2']) + _biases['out2'], name='fc22')
    print_activations(fc22)

    fc23 = tf.nn.relu(tf.matmul(fc1, _weights['out3']) + _biases['out3'], name='fc23')
    print_activations(fc23)

    fc24 = tf.nn.relu(tf.matmul(fc1, _weights['out4']) + _biases['out4'], name='fc24')
    print_activations(fc24)

    out = tf.concat(axis=1, values=[fc21, fc22, fc23, fc24], name='out')
    print_activations(out)
    return out

def accuracy_func(_pred, _y):
    y = tf.reshape(_y, shape=[-1, 4, 62])
    pred = tf.reshape(_pred, shape=[-1, 4, 62])
    correct_pred = tf.equal(tf.argmax(pred,2), tf.argmax(y,2))
    accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
    return accuracy

pred = ocr_net(x, weights, biases)

cost = -tf.reduce_mean(y*tf.log(pred))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

#correct_pred = tf.equal(tf.argmax(pred,2), tf.argmax(y,2))
#accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
accuracy = accuracy_func(pred, y)

init = tf.global_variables_initializer()

config = tf.ConfigProto()
config.gpu_options.allow_growth = True

with tf.Session(config=config) as sess:
    sess.run(init)
    step = 1# Keep training until reach max iterations
    while step * batch_size < training_iters:
        batch = data_train.next_batch(batch_size)
        # Fit training using batch data
        sess.run(optimizer, feed_dict={x: batch[0], y: batch[1]})
        if step % display_step == 0:
            # Calculate batch accuracy
            acc = sess.run(accuracy, feed_dict={x: batch[0], y: batch[1]})
            # Calculate batch loss
            loss = sess.run(cost, feed_dict={x: batch[0], y: batch[1]})
            print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc)
        step += 1
    print "Optimization Finished!"

    test_batch = data_test.next_batch(500)
    print "Testing Accuracy:", sess.run(accuracy, feed_dict={x: test_batch[0], y: test_batch[1]})

预测

tbd

tensorflow学习(四)自己创建CNN网络

之前已经介绍过数据的加载和可视化的问题,这次该研究模型了。CNN网络应该是深度学习的一个经典了,话不多说,直接介绍核心模块。

权重初始化

def weight_variable(shape):
    initial = tf.truncated_normal(shape, dtype=tf.float32, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial, trainable=True)

weights = {
    'wc1': weight_variable([3, 3, 1, 64]),
    'wc2': weight_variable([3, 3, 64, 128]),
    'wd1': weight_variable([4*4*12, 1024]),
    'wd2': weight_variable([1024, 1024]),
    'out': weight_variable([1024, 10])
}
biases = {
    'bc1': bias_variable([64]),
    'bc2': bias_variable([128]),
    'bd1': bias_variable([1024]),
    'bd2': bias_variable([1024]),
    'out': bias_variable([n_classes])
}

卷积层

这里的卷积参数使用1步长(stride size),0边距(padding size)的模板,保证输出和输入是同一个大小。

def conv2d(x, W, B, name):
    with tf.name_scope(name) as scope:
        conv = tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
        bias = tf.nn.bias_add(conv, B)
        conv = tf.nn.relu(bias, name=scope)
        return conv

conv1 = conv2d(images, weights['wc1'], biases['bc1'], 'conv1')

tensorflow里的padding参数支持SAMEVALID两种模式,设输入矩阵大小为W*W,卷积滤波器大小F*F,步长stride大小为S,则

# padding = 'VALID'
new_height = new_width = (W – F + 1) / S (结果向上取整)
# padding = 'SAME'
pad_needed_height = (new_height – 1) × S + F - W (结果向上取整,并在四周添加像素)

当new_height为奇数的时候,在顶部贴两个像素,在底部贴3个像素,而caffe和cuDNN是在两边各贴两个像素。这点不同,导致用caffe训练的模型转tensorflow的时候要慎用‘SAME’模式,切记!

池化

池化分为均值池化tf.nn.avg_pool和max池化tf.nn.max_pool。这里的池化用简单传统的2x2大小的模板做max pooling。

def max_pool(x, k, name):
    return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

def avg_pool(x, k, name):
    return tf.nn.avg_pool(x, ksize=[1, k, k, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

pool1 = max_pool(conv1, k=2, 'pool1')

归一化

def norm(x, lsize, name):
    return tf.nn.lrn(x, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name)

norm1 = norm(pool1, lsize=4, 'norm1')

全连接层

现在加一个1024维的全连接层,在池化层和全连接层连接处要将池化层输出的张量reshape成一向量

pool2_flat = tf.reshape(pool2, [-1, weights['wd1'].get_shape().as_list()[0]])

乘上权重矩阵,加上偏置,然后对其使用ReLU。

fc1 = tf.nn.relu(tf.matmul(pool2_flat, weights['wd1']) + biases['bd1'])

Dropout

用一个placeholder来代表一个神经元的输出在dropout中保持不变的概率。这样我们可以在训练过程中启用dropout,在测试过程中关闭dropout。 tensorflow学习(四)创建自己的CNN网络及可视化tf.nn.dropout操作除了可以屏蔽神经元的输出外,还会自动处理神经元输出值的scale。所以用dropout的时候可以不用考虑scale。

keep_prob = tf.placeholder("float")
fc1_drop = tf.nn.dropout(fc1, keep_prob)

Batch Normalization

《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》文章中的batch normalization可以提高训练的收敛速度,甚至可以不使用dropout和L2正则就能取得很好的泛化能力,参考中文资料http://blog.csdn.net/happynear/article/details/44238541。具体需要计算每个batch在该层所有特征图的均值和标准差,然后将上一层的输出归一化之后再送入下一层。

具体在tensorflow中,用tf.nn.moments求出axes对应的特征图维度的均值mean和标准差variance,然后用tf.nn.batch_normalization进行归一化,其中offset一般初始化为0,scale初始化为1,另外offset、scale的shape与mean相同,variance_epsilon这个参数设为一个很小的数就行,比如0.001。
需要强调一点的是,BN在神经网络进行training和testing的时候,所用的mean、variance是不一样的!以上的batch_normalization在训练的时候有效,测试阶段,只有一个样本输入,这时候网络参数固定,用之前训练好的均值和标准差作为参数传入即可。类似图片样本测试时候需要先减去均值,而这个均值文件是由训练样本生成的,差不多的道理。有一个不错的代码实现http://r2rt.com/implementing-batch-normalization-in-tensorflow.html

def batch_norm_wrapper(inputs, is_training, decay = 0.999):
    scale = tf.Variable(tf.ones([inputs.get_shape()[-1]]))
    beta = tf.Variable(tf.zeros([inputs.get_shape()[-1]]))
    pop_mean = tf.Variable(tf.zeros([inputs.get_shape()[-1]]), trainable=False)
    pop_var = tf.Variable(tf.ones([inputs.get_shape()[-1]]), trainable=False)

    if is_training:
        batch_mean, batch_var = tf.nn.moments(inputs,[0])
        train_mean = tf.assign(pop_mean, pop_mean * decay + batch_mean * (1 - decay))
        train_var = tf.assign(pop_var, pop_var * decay + batch_var * (1 - decay))
        with tf.control_dependencies([train_mean, train_var]):
            return tf.nn.batch_normalization(inputs, batch_mean, batch_var, beta, scale, epsilon)
    else:
        return tf.nn.batch_normalization(inputs, pop_mean, pop_var, beta, scale, epsilon)

bn_z1 = batch_norm_wrapper(z1, is_training)

输出层

普通输出层

out = tf.matmul(fc2_drop, _weights['out']) + _biases['out']

使用softmax层

out = tf.nn.softmax(tf.matmul(fc2_drop, weights['out']) + biases['out'])

训练

定义损失函数,这里要注意在输出层是否使用了softmax,这里不要重复使用。这里使用的损失函数是真实值y与预测值y_pred之间的交叉熵。注意tf.reduce_sum是把minibatch里的每张图的交叉熵都加进来的。

y_pred = tf.nn.softmax(pred)
cost = -tf.reduce_sum(y * tf.log(y_pred))

或者调用系统函数更为方便,如下

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))

tensorflow有大量的微分迭代优化算法,来对交叉熵进行梯度下降。

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(cost)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

模型训练通过反复执行optimizer来更新参数:

with tf.Session() as sess:
    sess.run(init)
    for i in range(1000):
        batch = mnist.train.next_batch(50)
        sess.run(optimizer, feed_dict={x: batch[0], y: batch[1], keep_prob: dropout})

tensorflow的SessionInteractiveSession方式不同。前者是构建完图对象后,通过run()整体执行全部操作,并通过close()释放资源。后者更为方便的进行交互,使用Tensor.eval()Operation.run()方法代替Session.run()。在DQN这类算法中由于每次迭代都需要交互,所以使用InteractiveSession

评估模型

通过tf.argmax函数获得输出层中的最大值的索引位置,并将所有的测试样本的结果取平均值得到测试集上的准确率。注意在测试集中dropout参数设置为1,也就是没有dropout。

correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
acc = sess.run(accuracy, feed_dict={x: batch[0], y: batch[1], keep_prob: 1.})

MyNet网络完整训练代码

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

import tensorflow as tf
# Parameters
learning_rate = 0.001
training_iters = 200000
batch_size = 64
display_step = 20

# Network Parameters
n_input = 784 # MNIST data input (img shape: 28*28)
n_classes = 10 # MNIST total classes (0-9 digits)
dropout = 0.8 # Dropout, probability to keep units

# tf Graph input
x = tf.placeholder(tf.float32, [None, n_input])
y = tf.placeholder(tf.float32, [None, n_classes])
keep_prob = tf.placeholder(tf.float32) # dropout (keep probability)

def weight_variable(shape):
    initial = tf.truncated_normal(shape, dtype=tf.float32, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial, trainable=True)

def conv2d(x, W, B, name):
    with tf.name_scope(name) as scope:
        conv = tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
        bias = tf.nn.bias_add(conv, B)
        conv = tf.nn.relu(bias, name=scope)
        return conv

def max_pool(x, k, name):
    return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

def avg_pool(x, k, name):
    return tf.nn.avg_pool(x, ksize=[1, k, k, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

def norm(x, lsize, name):
    return tf.nn.lrn(x, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name)

def my_net(_x, _weights, _biases, _dropout):
    _x = tf.reshape(_x, shape=[-1, 28, 28, 1])

    conv1 = conv2d(_x, _weights['wc1'], _biases['bc1'], 'conv1')
    pool1 = max_pool(conv1, k=2, name='pool1')
    norm1 = norm(pool1, lsize=4, name='norm1')

    conv2 = conv2d(norm1, _weights['wc2'], _biases['bc2'], 'conv2')
    pool2 = max_pool(conv2, k=2, name='pool2')
    norm2 = norm(pool2, lsize=4, name='norm2')

    pool2_flat = tf.reshape(norm2, [-1, _weights['wd1'].get_shape().as_list()[0]])
    fc1 = tf.nn.relu(tf.matmul(pool2_flat, _weights['wd1']) + _biases['bd1'])
    fc1_drop = tf.nn.dropout(fc1, _dropout)

    fc2 = tf.nn.relu(tf.matmul(fc1_drop, _weights['wd2']) + _biases['bd2'])
    fc2_drop = tf.nn.dropout(fc2, _dropout)

    out = tf.matmul(fc2_drop, _weights['out']) + _biases['out']
    return out

weights = {
    'wc1': weight_variable([3, 3, 1, 64]),
    'wc2': weight_variable([3, 3, 64, 128]),
    'wd1': weight_variable([7*7*128, 1024]),
    'wd2': weight_variable([1024, 1024]),
    'out': weight_variable([1024, 10])
}
biases = {
    'bc1': bias_variable([64]),
    'bc2': bias_variable([128]),
    'bd1': bias_variable([1024]),
    'bd2': bias_variable([1024]),
    'out': bias_variable([n_classes])
}

# Construct model
pred = my_net(x, weights, biases, keep_prob)

# Define loss and optimizer
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)

# Evaluate model
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

# Initializing the variables
init = tf.initialize_all_variables()

# Launch the graph
with tf.Session() as sess:
    sess.run(init)
    step = 1# Keep training until reach max iterations
    while step * batch_size < training_iters:
        batch = mnist.train.next_batch(batch_size)
        # Fit training using batch data
        sess.run(optimizer, feed_dict={x: batch[0], y: batch[1], keep_prob: dropout})
        if step % display_step == 0:
            # Calculate batch accuracy
            acc = sess.run(accuracy, feed_dict={x: batch[0], y: batch[1], keep_prob: 1.})
            # Calculate batch loss
            loss = sess.run(cost, feed_dict={x: batch[0], y: batch[1], keep_prob: 1.})
            print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc)
        step += 1
    print "Optimization Finished!"
    # Calculate accuracy for 256 mnist test images
    print "Testing Accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images[:256], y: mnist.test.labels[:256], keep_prob: 1.})

tensorflow学习(三)TensorBoard可视化

利用TensorBoard可以更好的了解深度学习训练过程以及参数的变化。思想是将tensorflow中的图的节点通过summary操作汇总到一起,再通过Ssummary.FileWriter写进protobuf,最后通过TensorBoard展示可视化结果。

Summary操作

tf.summary.scalar(name, values, collections=None)
tf.summary.image(name, tensor, max_outputs=None, collections=None)
tf.summary.histogram(name, values, collections=None)
tf.summary.merge(inputs, collections=None, name=None)
tf.summary.merge_all(key='summaries')

其中summary.scalar显示任意一个向量的数据,比如学习率或者loss。
其中summary.image可以显示节点中的4维的ndarray的数据。
其中summary.histogram用来显示多个数据的分布情况
以上创建的节点通过merge_all进行汇总合并。

SummaryWriter类

通过合并汇总后的protobuf对象传递给tf.summary.FileWriter。其参数logdir指定事件存储路径,add_graph方法控制显示的图像。并通过session.run和add_summary
写入。

Demo

这里我们用一个loss accuracy做一个summary的TensorBoard的demo,代码如下:

# normal step
...
init = tf.global_variables_initializer()

tf.summary.scalar('loss', cost)
tf.summary.scalar('accuracy', accuracy)
merged_summary_op = tf.summary.merge_all()

with tf.Seesion() as sess:
    sess.run(init)

    summary_writer = tf.summary.FileWriter('/tmp/logs')
    summary_writer.add_graph(sess.graph)

    while step * batch_size < training_iters:
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, keep_prob: dropout})
        if step % display_step == 0:
            acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})
            loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})

            summary_str = sess.run(merged_summary_op, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})
            summary_writer.add_summary(summary_str, step)

        step += 1

启动TensorBoard

执行命令:

python tensorflow/tensorboard/tensorboard.py --logdir=/tmp/logs

或者直接执行

tensorboard --logdir=/tmp/logs

然后通过浏览器打开localhost:6006,出来可视化界面后,就简单直观了,enjoy it!

tips

  • 之前我的cnn代码里有valid_prediction,所以画出来的graph有两条分支,不太清晰,所以只留了train一个分支
  • 多用with,进行包裹,这样才好看,正如官网说的,你的summary代码决定了你的图结构
  • 不是所有的tensor都有必要记录,但是Variable和placeholder最好都用summary记录一下,也是为了好看

tensorflow学习(二)数据接口

网上的教程、官方的资料大多关注的都是如何使用模型,对于数据源的说明都比较简单。基本资料都是用的MNIST数据集做的实验。

供给数据概述

tensorflow的模型数据读取还是非常友好的,最适合大量的数据的变量操作的形式应该是feed机制,利用tf.placeholder()为操作的变量创建占位符,在run()eval()的时候调用参数,将变量x和其真实标签y_传入模型。代码如下:

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

...

x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])

...

for i in range(1000):
    batch = mnist.train.next_batch(50)
    train_step.run(feed_dict={x:batch[0], y_: batch[1]})

上述代码解析:
首先将图像数据转换到4维的uint8的ndarray类型数组[index, y, x, depth]。标签数据根据是否为one_hot,转换为一个uint8的整型数值或者变成一个只有一个数值为1,其他维度为0的向量。然后将这样的数据标签对保存为tensorflow的DataSet类中。本例中这样的操作通过mnist = input_data.read_data_sets('MNIST_data', one_hot=True)实现。然后将这样的数据通过占位符feed进模型。

读取文件列表

我们可以通过一个文件列表来读入数据,之前都是通过python的glob来读取文件列表,也可以通过tf.train.match_filenames_once产生文件列表,然后交给tf.train.string_input_producer产生合适大小和是否乱序的文件名队列。

读取文件

图片文件

读入后为numpy的array格式,范围[-1,1],通道为[H,W,channels],代码如下

def imread(self, file_name):
    #    image : an image with type np.float32 in range [-1, 1]
    #    of size (H x W x 3) in RGB or
    #    of size (H x W x 1) in grayscale.
    img = skimage.img_as_float(skimage.io.imread(file_name, as_grey=False)).astype(np.float32)
    img = img*2.0 - 1.0
    img_resize = skimage.transform.resize(img, (width, height, 3))
    img_resize = img_resize[:, :, (2,1,0)]
    return img_resize

csv文件

使用tf.TextLineReader读取文件的一行内容,然后通过tf.decode_csv来解析这一行内容并将其转为张量列表

二进制文件读取固定长度

从二进制文件中读取固定长度纪录, 可以使用tf.FixedLengthRecordReadertf.decode_raw操作。decode_raw操作可以讲一个字符串转换为一个uint8的张量。例程中的cifar-10的数据集的读入就是通过这个方式。

标准TensorFlow格式TFRecords文件

将数据填入到Example协议内存块(protocol buffer),将协议内存块序列化为一个字符串,并且通过tf.python_io.TFRecordWriter写入到TFRecords文件。通过传参images和labels将数据和标签数据保存到tfrecords中,代码如下:

def _int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[value]))

def _bytes_feature(value):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

def convert_to(images, labels, name):
    num_examples = labels.shape[0]
    if images.shape[0] != num_examples:
        raise ValueError("Images size %d does not match label size %d." % (images.shape[0], num_examples))
    rows = images.shape[1]
    cols = images.shape[2]
    depth = images.shape[3]

    filename = os.path.join(FLAGS.directory, name + '.tfrecords')
    print('Writing', filename)
    writer = tf.python_io.TFRecordWriter(filename)
    for index in range(num_examples):
        image_raw = images[index].tostring()
        example = tf.train.Example(features=tf.train.Features(feature={
            'height': _int64_feature(rows),
            'width': _int64_feature(cols),
            'depth': _int64_feature(depth),
            'label': _int64_feature(int(labels[index])),
            'image_raw': _bytes_feature(image_raw)}))
        writer.write(example.SerializeToString())
    writer.close()

从TFRecords文件中读取数据,可以使用tf.TFRecordReadertf.parse_single_example解析器。这个parse_single_example操作可以将Example协议内存块(protocol buffer)解析为张量。通过train参数传入TFRecords文件返回值是图像标签对。代码如下:

def read_and_decode(filename_queue):
    reader = tf.TFRecordReader()
    _, serialized_example = reader.read(filename_queue)
    features = tf.parse_single_example(
        serialized_example,
        # Defaults are not specified since both keys are required.
        features={
            'image_raw': tf.FixedLenFeature([], tf.string),
            'label': tf.FixedLenFeature([], tf.int64),
        })

    # Convert from a scalar string tensor (whose single string has
    # length mnist.IMAGE_PIXELS) to a uint8 tensor with shape
    # [mnist.IMAGE_PIXELS].
    image = tf.decode_raw(features['image_raw'], tf.uint8)
    image.set_shape([mnist.IMAGE_PIXELS])

    # Convert from [0, 255] -> [-0.5, 0.5] floats.
    image = tf.cast(image, tf.float32) * (1. / 255) - 0.5
    # Convert label from a scalar uint8 tensor to an int32 scalar.
    label = tf.cast(features['label'], tf.int32)

    return image, label


def inputs(train, batch_size, num_epochs):
    if not num_epochs: num_epochs = None
    filename = os.path.join(FLAGS.train_dir, TRAIN_FILE if train else VALIDATION_FILE)

    with tf.name_scope('input'):
        filename_queue = tf.train.string_input_producer([filename], num_epochs=num_epochs)
        image, label = read_and_decode(filename_queue)

        return image, label

批处理

TBD

创建线程并使用QueueRunner对象来预取

TBD

训练模型保存和恢复

通过实例化一个tf.train.Saver来实现模型的保存。

saver = tf.train.Saver()
saver.save(sess, FLAGS.train_dir, global_step=step)

在训练循环中,将定期调用saver.save()方法,向训练文件夹中写入包含了当前所有可训练参数的检查点文件。
恢复检查点的时候使用saver.restore()方法,重载模型的参数,继续训练。

saver.restore(sess, FLAGS.train_dir)

参考资料

http://wiki.jikexueyuan.com/project/tensorflow-zh/how_tos/reading_data.html

tensorflow学习(一)安装配置

tensorflow是google开源的深度学习平台,由于其性能比较差,一开始我是拒绝的,不过由于google强大的影响力和执行力,版本迭代更新很快,现在性能已经很快了,而且最新的版本也支持了分布式。而且tensorflow的中文文档也是所有开源框架里最完善的,所以近期抽空学习一下。还是先从安装搞起。
虽然其有比较完善的官方文档https://www.tensorflow.org/以及其中文版本http://wiki.jikexueyuan.com/project/tensorflow-zh/,但使用的时候还是发现存在一些坑,还是记录一下学习过程中遇到的问题。
tensorflow支持pip、virtualenv、Docker三种安装方式。我这里只尝试了最简单方便的pip安装。

安装环境

我的电脑是centOS 7.0,k40双卡

CUDA和cudnn

参考之前教程,安装CUDA和cudnn。我这里的CUDA版本是7.0,cudnn版本是v3。
再安装six和protobuf到对应版本,tensorflow要求’six >= 1.10.0, protobuf >= 3.0.0b2’,网上下载对应的whl包,执行

pip install six-1.10.0-py2.py3-none-any.whl
pip install protobuf

pip安装tensorflow

根据官方教程,应该执行如下命令

# 仅使用 CPU 的版本
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.7.1-cp27-none-linux_x86_64.whl
# 开启 GPU 支持的版本 (安装该版本的前提是已经安装了 CUDA sdk)
pip install https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow-0.7.1-cp27-none-linux_x86_64.whl

但是由于GFW原因,上述命令可能会超时失败,于是寻找其他whl包的位置。
在官方的github上很容易找到其对应的安装版本。https://github.com/tensorflow/tensorflow,我这里使用的版本是python2的GPU版本。

# 开启 GPU 支持的版本 (安装该版本的前提是已经安装了 CUDA sdk)
pip install http://ci.tensorflow.org/view/Nightly/job/nigntly-matrix-linux-gpu/TF_BUILD_CONTAINER_TYPE=GPU,TF_BUILD_IS_OPT=OPT,TF_BUILD_IS_PIP=PIP,TF_BUILD_PYTHON_VERSION=PYTHON2,label=gpu-slave/lastSuccessfulBuild/artifact/pip_test/whl/tensorflow-0.7.1-cp27-none-linux_x86_64.whl

安装成功。
但是在import tensorflow的时候会报错说ImportError: libcudart.so.7.5: cannot open shared object file: No such file or directory,可恶,我明明装的是CUDA7.0,竟然报错找不到7.5,解决这个问题也很简单,只需要把7.0做个软链接弄个名7.5骗过tensorflow就行,代码如下:

sudo ln -s /usr/local/cuda/lib64/libcudart.so.7.0 /usr/local/cuda/lib64/libcudart.so.7.5
sudo ln -s /usr/local/cuda/lib64/libcublas.so.7.0 /usr/local/cuda/lib64/libcublas.so.7.5

运行tensorflow

在python终端执行如下代码:

import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print sess.run(hello)
a = tf.constant(10)
b = tf.constant(32)
print sess.run(a+b)

顺利运行表明tensorflow安装完成。

源码路径以及示例

由于我的python是anaconda版本,所以tensorflow会安装在’$HOME/anaconda/lib/python2.7/site-packages/tensorflow’。
运行第一个示例,如下:

cd $HOME/anaconda/lib/python2.7/site-packages/tensorflow
python ./models/image/mnist/convolutional.py

程序会下载mnist数据集,迭代训练,显示当前loss、学习率以及误差。

GLIBC的版本问题解决方案

在CentOS/RHEL 6的操作系统中,GLIBC的版本可能不满足tensorflow的要求,在import tensorflow的时候会报错

ImportError: /lib64/libc.so.6: version `GLIBC_2.15' not found (required by /home/15072585/anaconda/lib/python2.7/site-packages/tensorflow/python/_pywrap_tensorflow.so)

通过命令查看/lib64/libc.so.6的GLIBC版本:

strings /lib64/libc.so.6 |grep GLIBC

可以看到其只支持到2.12,不能支持高版本。解决方案:

mkdir ~/my_libc_env
cd ~/my_libc_env
wget http://launchpadlibrarian.net/137699828/libc6_2.17-0ubuntu5_amd64.deb
wget http://launchpadlibrarian.net/137699829/libc6-dev_2.17-0ubuntu5_amd64.deb
wget ftp://rpmfind.net/linux/sourceforge/m/ma/magicspecs/apt/3.0/x86_64/RPMS.lib/libstdc++-4.8.2-7mgc30.x86_64.rpm
ar p libc6_2.17-0ubuntu5_amd64.deb data.tar.gz | tar zx
ar p libc6-dev_2.17-0ubuntu5_amd64.deb data.tar.gz | tar zx
rpm2cpio libstdc++-4.8.2-7mgc30.x86_64.rpm| cpio -idmv

以后想执行支持tensorflow的python版本的时候需要执行命令:

LD_LIBRARY_PATH="$HOME/my_libc_env/lib/x86_64-linux-gnu/:$HOME/my_libc_env/usr/lib64/" $HOME/my_libc_env/lib/x86_64-linux-gnu/ld-2.17.so `which python`

GLIBC版本升级注意事项

注意GLIBC这是一个根本的库,不能轻易升级。非要升级的话,首先查看当前的libc的指向

ll /lib64/libc.so.6

获取高级版本的libc.so,比如/lib64/libc-2.15.so,这个步骤非常繁琐,编译生成很麻烦。我是通过从另外高级版本的机器copy过来使用的,所以没有生成,过程隐去。
接下来就是将软链接指向新的libc,实际操作注意rm原来的链接之后,系统的大部分命令都无法使用了,要用LD_PRELOAD=/lib64/libc-2.15.so指定所使用的libc才能运行shell命令。代码如下:

rm -rf /lib64/libc.so.6
LD_PRELOAD=/lib64/libc-2.15.so ln -s/lib64/libc-2.15.so  lib64/libc.so.6

基于源码安装(不建议)

安装jdk

bazel依赖于jdk,网上都是安装JDK 8,由于centos默认是JDK 7,并且我们项目用的java版本也是1.7,所以去bazel官网看了一下,也是支持JDK 7的。所以这里没安装java

安装bazel

git clone https://github.com/bazelbuild/bazel.git
cd bazel
./compile.sh
sudo
cp output/bazel /usr/local/bin/

编译tensorflow

git clone https://github.com/tensorflow/tensorflow
cd tensorflow
./configure
# choose with Google Cloud Platform / with GPU
bazel build -c opt --config=cuda //tensorflow/tools/pip_package:build_pip_package
bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
pip install /tmp/tensorflow_pkg/tensorflow-0.10.0-py2-none-any.whl

高维海量数据快速检索库

falconn

这个库使用的是局部敏感哈希构建索引。效率较高。

python实现

官方的api文档https://falconn-lib.org/pdoc/falconn/其实非常详细,这里把其中关键的地方再提炼一下。
安装非常简单

pip install falconn

数据准备,需要将所有的索引特征存储到一个numpy的二维array中,其中每行代表一个数据,行数表示数据点数,列表示特征维度。注意,最好把数据转换为float32数据类型,提高算法性能,并将数据归一化减去均值,提高算法性能。具体操作如下

dataset = dataset.astype(numpy.float32)
dataset -= numpy.mean(dataset, axis=0)

构建哈希超参数,将数据维度和点数传入get_default_parameters获得合适的超参数,得到的超参数可以通过手动微调或者compute_number_of_hash_functions来对其中细节进行微调。

num_points, dim = dataset.shape
parms = falconn.get_default_parameters(num_points, dim)
falconn.compute_number_of_hash_functions(7, parms)

创建索引实例LSHIndex,并通过方法setup构建索引。

lsh_index = falconn.LSHIndex(parms)
lsh_index.setup(dataset)

查询的函数有很多,可以根据需要进行选择

find_k_nearest_neighbors()
find_near_neighbors()
find_nearest_neighbor()
get_candidates_with_duplicates()
get_unique_candidates()

C++实现

TBD

Panns

https://github.com/ryanrhymes/panns这是一个用python写的针对高维数据的approximate k-nearest neighbors算法包,目前支持欧式距离和余弦距离两种度量。对500维以上的高维特征进行了优化。虽然性能弱于Annoy,但是更加轻量,比FLANN和scikit-learn更为好用

安装

sudo pip install panns --upgrade

使用示例

from panns import *

# create an index of Euclidean distance
p = PannsIndex(dimension=100, metric='euclidean')

# generate a 1000 x 100 dataset
for i in xrange(1000):
    v = gaussian_vector(100)
    p.add_vector(v)

p.parallelize(True)        # 多核并行,默认是False
# build an index of 128 trees and save to a file
p.build(128)    # 64 is default

build之后调用save函数保存生成的索引,会生成两个文件.idx保存索引树和.idx.npy保存数据库向量矩阵,这个向量矩阵可以保存成内存文件或者mmap文件,根据如下注释选取保存类型。这两个文件要放在同一个目录下面,加载模型的时候才不会出错。

# save the index as an in-memory file if the raw dataset is small or medium size
# later panns will load the entire .npy file in to the physical memory
p.save('test.idx', mmap=False)

# save the index as mmap file if the raw dataset is huge
# usually, your OS will handle the dynamic loading
p.save('test.idx', mmap=True)

加载模型,查询索引,返回最近邻的前10个查询结果

p1 = PannsIndex(metric='euclidean')
p1.load('test.idx')
v1 = a_new_vector(100)
n = p1.query(v1, 10)

Annoy

https://github.com/spotify/annoy这是一个用C++编写的python打包的最近邻搜索算法包,应该是目前性能最好的算法包。

安装

pip install annoy

实例

tbd

参考资料

机器视觉:十亿规模的深度描述子如何有效索引

数据可视化

经常看到网上一些样本分布图画的非常漂亮,不知道是怎么画出来的,在网上找到了一个好资料http://colah.github.io/posts/2014-10-Visualizing-MNIST/#raw_mnist,代码参考的这个https://www.oreilly.com/learning/an-illustrated-introduction-to-the-t-sne-algorithm研究了一番总结一下。

我们经常要分析一些高维特征的表现能力,由于我们人类只能理解二维、三维的东西,所以要把高维数据降到三维以内展示出来。

直观想法

最直观的想法,比如原数据有1000维,从中任意选择两维,然后画图,结果可想而知。

PCA

一种改进办法就是选择更为合适的两个维度进行展示,于是选择PCA主成分分析,然后选择前两个主成分对应的维度作为可视化的两个维度。

降维优化算法

可视化的目标就是希望两个向量实际的距离能够跟显示的距离接近,本来向量距离远的显示的距离也远。这就变成了一个优化问题,这也是很多流形学习方法的目标。比较常用解决这个优化问题的方法有MDS和t-SNE

MDS

结合代码分析,首先,加载必要模块

from sklearn import manifold
import matplotlib.pyplot as plt
import seaborn
import numpy as np

首先加载数据,用array存储数据以及对应标签。这里用scikit-learn中的digits手写字符数据集作为演示数据,加载之后,其data维度为179764,图片是88=64,target维度为1797*1,为0~9的整数。同时加载seaborn模块中的调色板,用这个模板生成的颜色比自己设计的要好看一些

from sklearn.datasets import load_digits
digits = load_digits()
palette = np.array(seaborn.color_palette('hls', 10))
x = digits.data
y = digits.target

加载mds模型

mds = manifold.MDS(n_components=2, max_iter=300, eps=1e-5)
x_mds = mds.fit_transform(x)

画图

fig = plt.figure()
plt.scatter(x_mds[:,0], x_mds[:,1], c=palette[y])
plt.show()

t-SNE

加载数据与上一种方法相同,加载模型代码如下

tsne = manifold.TSNE(n_components=2, learning_rate=1000.0)
x_tsne = tsne.fit_transform(x)

显示动态流形变化图像

TBD

可视化参考资料

how to use t-SNE effectively中文翻译

darknet学习(一)YOLO目标检测

darknet安装

darknet的安装还是非常简单的,依赖只有cuda和opencv,安装好之后执行以下命令即可安装编译成功

git clone https://github.com/pjreddie/darknet.git
cd darknet
vim Makefile
#################[modify Makefile]
GPU=1
OPENCV=1
OPTS=-Ofast  # 如果gcc版本过低,这里可能需要改成-O3
#################[close]
make

YOLO训练

准备训练数据

检测算法需要对图像中的目标区域进行标注,我这里实验只准备了5个类别的检测数据,我用python基于OpenCV做了一个简单的标注工具,可以得到矩形左上角、右下角的坐标位置,代码如下:

import os
import cv2
import numpy as np
import copy
import glob
import sys


#PT1 = (0,0)
#PT2 = (0,0)
def draw_box(event, x, y, flags, param):
    global PT1
    global PT2
    global img_copy
    if event == cv2.EVENT_LBUTTONDOWN and flags == cv2.EVENT_FLAG_LBUTTON:    
        PT1 = (x,y)
        PT2 = (x,y)
    if flags == cv2.EVENT_FLAG_LBUTTON:
        PT2 = (x,y)
    if event == cv2.EVENT_LBUTTONUP:
        PT2 = (x,y)
    cv2.rectangle(img_copy, PT1, PT2, (0,255,0), 2)
    cv2.imshow('image', img_copy)
    img_copy = copy.deepcopy(img)


if __name__ == '__main__':
    img_path = 'D:/test'
    img_list = glob.glob(os.path.join(img_path, '*.jpg'))
    img_list = sorted(img_list)
#    print img_list
    box_result_name = 'box_result.txt'
    img_processed = []
    try:
        with open(os.path.join(img_path, box_result_name), 'rb') as f_box:
            for line in f_box.readlines():
                line = line.strip().split(',')
                img_processed.append(line[0])
    except IOError:
        pass
    img_processed = set(img_processed)

    cv2.namedWindow('image')
    cv2.setMouseCallback('image', draw_box)

    f_box = open(os.path.join(img_path, box_result_name), 'a')
    for img_name in img_list:
        print img_name
        if img_name in img_processed:
            continue
        img = cv2.imread(img_name)
        img_copy = copy.deepcopy(img)
        PT1 = (0,0)
        PT2 = (0,0)

        while (1):
            key = cv2.waitKey(0)
            if key == 27:
                cv2.destroyAllWindows()
                f_box.close()
                sys.exit(0)
            elif key == 32:
                if PT1[0]==0 or PT1[1]==0 or PT2[0]==0 or PT2[1]==0 or abs(PT2[0]-PT1[0])<10 or abs(PT2[1]-PT1[1])<10:
                    continue
                if PT1[0] < PT2[0]:
                    x1 = PT1[0]
                    x2 = PT2[0]
                else:
                    x1 = PT2[0]
                    x2 = PT1[0]
                if PT1[1] < PT2[1]:
                    y1 = PT1[1]
                    y2 = PT2[1]
                else:
                    y1 = PT2[1]
                    y2 = PT1[1]
                f_box.write('%s,%d,%d,%d,%d\n' % (img_name, x1, y1, x2, y2))
                #print 'next image'
                break
            elif key == 122:
                f_box.close()
                f_box = open(os.path.join(img_path, box_result_name), 'rb')
                lines = f_box.readlines()
                f_box.close()
                curr = lines[:-1]
                f_box = open(os.path.join(img_path, box_result_name), 'w')
                for line in lines:
                    line = line.strip()
                    f_box.write('%s' % line)
                print 'delete last image box'
                cv2.destroyAllWindows()
                f_box.close()
                sys.exit(0)
        else:
            break
    cv2.destroyAllWindows()
    f_box.close()

这个标注工具代码比较繁琐,不过还算鲁棒,可以有效处理误标注并实现自动跳转下一张图的功能。每一个待检测的类别用这个标注工具可以生成一个对应的文本文件说明每一张图的矩形坐标位置。

生成用于darknet的标注

darknet要求每一张图片a.jpg对应一个a.txt,文本里面一行信息说明groundtruth的类别和图像原始宽高的相对坐标,如下:

# <object-class> <x> <y> <width> <height>
1 0.526875 0.499375 0.30875 0.80375

我基于darknet自带的scripts\voc_label.py写了一个脚本把图像以及标注工具生成的矩形坐标转换成满足darknet的标注信息

import pickle
import os
from os import listdir, getcwd
from os.path import join

sets = ['men_jacket', 'men_bottem', 'underwear', 'women_bottem', 'women_jacket']
classes = ['men_jacket', 'men_bottem', 'underwear', 'women_bottem', 'women_jacket']


def convert(size, box):
    dw = 1./size[0]
    dh = 1./size[1]
    x = (box[0] + box[2])/2.0
    y = (box[1] + box[3])/2.0
    w = box[2] - box[0]
    h = box[3] - box[1]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

def convert_annotation(line, image_id, set_id):
    out_file = open('box_labels/%s/%s.txt'%(set_id, image_id), 'w')

    w = 800
    h = 800
    cls = line[0].split('/')[1]
    if cls not in classes:
        return
    cls_id = classes.index(cls)

    b = (float(line[1]), float(line[2]), float(line[3]), float(line[4]))
    bb = convert((w,h), b)
    out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
    out_file.close

wd = getcwd()
list_file = open('train.txt', 'w')
for set_id in sets:
    if not os.path.exists('box_labels/%s' % (set_id)):
        os.makedirs('box_labels/%s' % (set_id))
    images_info = open('box_imgs/%s.txt' % (set_id)).readlines()
    for line in images_info:
        line = line.strip().split(',')
        image_id = line[0].split('/')[2].split('.')[0]
        list_file.write('%s/box_imgs/%s/%s.jpg\n' % (wd, set_id, image_id))
        convert_annotation(line, image_id, set_id)
list_file.close()

执行这个脚本会生成一个box_labels文件夹,并在里面生成对应的darknet标签信息。同时生成一个train.txt说明用于训练的图片的路径。

更改YOLO模型参数

cfg/yolo.cfg的配置文件进行调整

subdivisions=2 # 原来为64,改小可以提高训练速度,同时增加显存使用
output= 735 # 最后connected层,原来值为1470
classes = 5 # detection层,这里设置为5

这个参数的确定由公式output = S x S x (5*B+C),其中S=7,B=2, C=5(5是我实验使用的类别数)。

改写src/data.c中的fill_truth_region函数,确定图像和标签数据的加载位置

//    char *labelpath = find_replace(path, "images", "labels");
    char *labelpath = find_replace(path, "box_imgs", "box_labels");

改写src/yolo.c

//char *voc_names[] = {"aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"};
//image voc_labels[20];
char *voc_names[] = {"men_jacket", "men_bottem", "underwear", "women_bottem", "women_jacket"};
image voc_labels[5];
void train_yolo(char *cfgfile, char *weightfile)
{
//    char *train_images = "/data/voc/train.txt";
//    char *backup_directory = "/home/pjreddie/backup/";
    char *train_images = "/home/xixi/darknet/train.txt";
    char *backup_directory = "/home/xixi/darknet/results/";

...

void run_yolo(int argc, char **argv)
{
    /*
    int i;
    for(i = 0; i < 20; ++i){
        char buff[256];
        sprintf(buff, "data/labels/%s.png", voc_names[i]);
        voc_labels[i] = load_image_color(buff, 0, 0);
    }
    */

改好之后重新编译代码,下载预训练好的模型参数文件,并开始训练

make
wget http://pjreddie.com/media/files/extraction.conv.weights
./darknet -i 2 yolo train cfg/yolo.cfg extraction.conv.weights

在results文件夹下面就好生成训练好的模型文件,当迭代40000次后,会输出最终模型文件yolo_final.weights。这个过程用GPU大概需要3天时间,消耗显存6个G。(-i参数表示使用第二块GPU)

YOLO检测

# 检测单张图片
./darknet yolo test cfg/yolo.cfg results/yolo_final.weights <image>
# 检测多张图片
./darknet yolo test cfg/yolo.cfg results/yolo_final.weights
# 改变检测的阈值
./darknet yolo test cfg/yolo.cfg results/yolo_final.weights <image> -thresh 0
# 改为CPU模式检测
./darknet -nogpu yolo test cfg/yolo.cfg results/yolo_final.weights <image>

经过测试,在GPU环境下,测试一张图片跑完整的YOLO模型需要40ms~100ms,CPU环境下需要5秒以上。

补充

YOLO官方后来又出了一个yolo2.cfg,研究了一下,每一卷积层都加了batch_normlization,提高训练速度,破费。同时把最后的一个全连接层改成了一个local层

讨论和思考

  • YOLO的核心思想就是利用整张图作为网络的输入,直接在输出层回归bounding box的位置和bounding box所属的类别。

  • 将一副图像经过类似alexnet的特征提取,最后经过一层的全连接层之后又映射回SxS个网格(grid cell),如果某个object的中心 落在这个网格中,则这个网格就负责预测这个object。

  • 每个网格要预测B个bounding box,每个bounding box除了要回归自身的位置之外,还要附带预测一个confidence值。 这个confidence等于所预测的box中含有object的置信度(object落在其中,取1,否则取0)和这个box和truth的IOU交集的乘积确定。这样每个网格要返回bounding box预测的(x, y, w, h)和confidence5个值,以及object的类别信息C类,所以最后一层的输出应该为S x S x (5*B+C)

  • 由于xywh取的是相对坐标,归一化为0~1,confidence取值范围为0~1,category的类别取值为0或1,简单实现了归一化的目的,然而由于目标函数使用了均方误差损失函数,位置坐标和类别判别信息维度对损失函数的贡献不应该是一样的,所以文章中采用了如下的方法改进

  1. 更重视坐标预测,给这些损失前面赋予更大的loss weight, 训练中取5。
  2. 对没有object的box的confidence loss,赋予小的loss weight,训练中取0.5。
  3. 有object的box的confidence loss和类别的loss的loss weight正常取1。
  • 对于小的box位置偏差对实际的效果影响更为严重,但均方误差对这个没有体现,所以对w和h参数又取了平方根处理,增大的小box的位置偏移对loss函数的影响。

  • 一个网格有多个bounding box的时候,根据IOU值取前B个进行处理。所以对多个物体相互靠近的时候处理的并不是十分理想。

  • 在test一张图片的时候,通过网络得到SxSx(5*B+C)层的值后,每个网格的类别信息Pr1,其对应的bounding box的confidence Pr2以及box和网格的IOU信息的乘积用来预测box属于某一类的概率。根据阈值过滤后,对保留的boxes进行非极大值抑制NMS处理,得到最终结果。

caffe学习(六)创建自己的模型

最近阅读文章《Supervised Learning of Semantics-Preserving Hashing via Deep Neural Networks for Large-Scale Image Search》,对应的开源代码有两个版本https://github.com/kevinlin311tw/caffe-cvprw15https://github.com/kevinlin311tw/Caffe-DeepBinaryCode两者之间的主要区别在于后者使用了更为复杂的目标函数,而不是简单的做了一层sigmoid。由于其在alexnet上进行了微小改动,在fc7和fc8层之间添加了一个latent layer,将原本用于检索的4096特征维度哈希为128维的二值特征,取得了不错的效果,借鉴这篇文章的代码,我实验了一下如何创建自己的模型,添加自己的layer

预训练模型

不同于文章使用的alexnet,我的实验使用的是更为复杂的googLeNet。首先要训练好一个模型,对应一个caffemodel文件,和其训练测试对应的prototxt文件,并在此之上进行fine-tune。

准备训练数据

跟普通的分类训练没有区别,也要准备lmdb格式的文件以及均值文件。

修改train.prototxt

原本的层级结构是pool5/7x7_s1 --> loss3/classifier --> SoftmaxWithLoss & Accuracy,改动后变成pool5/7x7_s1 --> latent_SSDH --> latent_SSDH_encode --> loss3/classifier_change --> SoftmaxWithLoss & Accuracy。具体就是添加了一层InnerProduct,输出数目为哈希的位数,后面接一层Sigmoid层将其二值化,再接到原本的loss3/classifier层,并对该层进行finetune,调整该层的训练参数。改动代码如下:

#########added by yx followed by layer "pool5/drop_7x7_s1"
layer {
  name: "latent_SSDH"
  type: "InnerProduct"
  bottom: "pool5/7x7_s1"
  top: "latent_SSDH"
  param {
    lr_mult: 1
    decay_mult: 1
  }
  param {
    lr_mult: 2
    decay_mult: 0
  }
  inner_product_param {
    num_output: 128
    weight_filler {
      type: "gaussian"
      std: 0.005
    }
    bias_filler {
      type: "constant"
      value: 1
    }
  }
}
layer {
  name: "latent_SSDH_encode"
  bottom: "latent_SSDH"
  top: "latent_SSDH_encode"
  type: "Sigmoid"
}
##########################

并把loss3/classifier层的名字改为loss3/classifier_change,bottom层改为latent_SSDH_encode,top层改为loss3/classifier_change,把lr_mult参数*10。

最后把SoftmaxWithLoss层以及Accuracy层的bottom改为新层名loss3/classifier_change

这里详细介绍一下weight_filter的初始化细节,一般有gaussianxavier两种方式:
xavier具体方式是从[-scale, +scale]中进行均匀采样,对卷积层或全连接层中参数进行初始化的方法。
其中scale = \sqrt(3 / n), n根据不同实现可设置为n=(num_in + num_out) / 2 (Understanding the difficulty of training deep feedforward neural networks ),或n=num_out (caffe最初实现方法)

修改solver.prototxt

  • test_initialization屏蔽掉
  • weight_decay改为0.0005
  • gamma改为0.1
  • base_lr参数降低10倍,改为0.001

修改deploy.prototxt

因为原本的deploy是要做分类的任务,而我这里是要做哈希特征提取,所以要把原来的最后两层loss3/classifier_change和Softmax屏蔽掉,并添加如下代码

############## added by yx #########
layer {
  name: "latent_SSDH"
  type: "InnerProduct"
  bottom: "pool5/7x7_s1"
  top: "latent_SSDH"
  inner_product_param {
    num_output: 128
  }
}
layer {
  name: "latent_SSDH_encode"
  type: "Sigmoid"
  bottom: "latent_SSDH"
  top: "latent_SSDH_encode"
}

训练命令

nohup ../../build/tools/caffe train --solver=solver.prototxt -weights sku30450_googlenet_quick_iter_500000.caffemodel -gpu 1 &

linux shell技巧总结

匹配特殊字符空格

find . -name "*[[:space:]]*"

找到指定文件并删除

find . -name "abc*" -exec rm {} \;

大文件分割与合并

把aaa.tar.gz分割,每卷5G,然后合并

split -b 5000m aaa.tar.gz aaa_part.tar.gz.
cat aaa_part.tar.gz.* > aaa.tar.gz

大文件夹aaa分卷压缩与合并解压

tar czvf - aaa | split -b 5000m - aaa_part.tar.gz.
cat aaa_part.tar.gz.* | tar xz

批量杀死进程

ps aux | grep "common" | cut –c 9-15 | xargs kill

find 文件查找

查找txt和pdf文件

find . ( -name "*.txt" -o -name "*.pdf" ) -print

正则方式查找.txt和pdf,-iregex: 忽略大小写的正则

find . -regex  ".*(.txt|.pdf)$"

否定参数,查找所有非txt文本

find . ! -name "*.txt" -print

指定搜索深度,打印出当前目录的文件(深度为1)

find . -maxdepth 1 -type f

按类型搜索:-type f 文件 / l 符号链接 / d 目录

find . -type d -print  //只列出所有目录

按时间搜索:
-atime 访问时间 (单位是天,分钟单位则是-amin,以下类似)
-mtime 修改时间(内容被修改)
-ctime 变化时间(元数据或权限变化)
最近7天被访问过的所有文件:

find . -atime 7 -type f -print

按大小搜索:w字 k M G,寻找大于2k的文件

find . -type f -size +2k

按权限查找:

find . -type f -perm 644 -print //找具有可执行权限的所有文件

按用户查找:

find . -type f -user weber -print// 找用户weber所拥有的文件

找到后的后续动作

删除:删除当前目录下所有的swp文件:

find . -type f -name "*.swp" -delete

执行动作(强大的exec)

find . -type f -user root -exec chown weber {} ; //将当前目录下的所有权变更为weber

注:{}是一个特殊的字符串,对于每一个匹配的文件,{}会被替换成相应的文件名;
eg:将找到的文件全都copy到另一个目录:

find . -type f -mtime +10 -name "*.txt" -exec cp {} OLD ;

结合多个命令
tips: 如果需要后续执行多个命令,可以将多个命令写成一个脚本。然后 -exec 调用时执行脚本即可;

-exec ./commands.sh {} \;

-print的定界符

默认使用’\n’作为文件的定界符;
-print0 使用”作为文件的定界符,这样就可以搜索包含空格的文件;

grep 文本搜索

常用参数
-o 只输出匹配的文本行
-v 只输出没有匹配的文本行
-c 统计文件中包含文本的次数

grep -c "text" filename

-n 打印匹配的行号
-i 搜索时忽略大小写
-l 只打印文件名
在多级目录中对文本递归搜索(程序员搜代码的最爱):

grep "class" . -R -n

匹配多个模式

grep -e "class" -e "vitural" file

grep输出以作为结尾符的文件名:(-z)

grep "test" file* -lZ| xargs -0 rm

xargs 命令行参数转换

xargs 能够将输入数据转化为特定命令的命令行参数;这样,可以配合很多命令来组合使用。比如grep,比如find;

将多行输出转化为单行输出

cat file.txt| xargs

将单行转化为多行输出,-n:指定每行显示的字段数

cat single.txt | xargs -n 3

xargs参数说明

-d 定义定界符 (默认为空格 多行的定界符为 n)
-n 指定输出为多行
-I {} 指定替换字符串,这个字符串在xargs扩展时会被替换掉,用于待执行的命令需要多个参数时。eg:

cat file.txt | xargs -I {} ./command.sh -p {} -1

-0:指定为输入定界符,eg:统计程序行数

find source_dir/ -type f -name "*.cpp" -print0 |xargs -0 wc -l

sort 排序

字段说明:
-n 按数字进行排序 VS -d 按字典序进行排序
-r 逆序排序
-k N 指定按第N列排序
eg:

sort -nrk 1 data.txt
sort -bd data // 忽略像空格之类的前导空白字符

uniq 消除重复行

消除重复行

sort unsort.txt | uniq

统计各行在文件中出现的次数

sort unsort.txt | uniq -c

找出重复行,可指定每行中需要比较的重复内容:-s 开始位置 -w 比较字符数

sort unsort.txt | uniq -d

用tr进行转换

通用用法

echo 12345 | tr '0-9' '9876543210' //加解密转换,替换对应字符
cat text| tr 't' ' '  //制表符转空格

tr删除字符

cat file | tr -d '0-9' // 删除所有数字

-c 求补集

cat file | tr -c '0-9' //获取文件中所有数字
cat file | tr -d -c '0-9 n'  //删除非数字数据

tr压缩字符
tr -s 压缩文本中出现的重复字符;最常用于压缩多余的空格

cat file | tr -s ' '

字符类
tr中可用各种字符类:
alnum:字母和数字
alpha:字母
digit:数字
space:空白字符
lower:小写
upper:大写
cntrl:控制(非可打印)字符
print:可打印字符
使用方法:tr [:class:] [:class:]

eg: tr '[:lower:]' '[:upper:]'

cut 按列切分文本

截取文件的第2列和第4列:

cut -f2,4 filename

去文件除第3列的所有列:

cut -f3 --complement filename

-d 指定定界符:

cat -f2 -d";" filename

cut 取的范围
N- 第N个字段到结尾
-M 第1个字段为M
N-M N到M个字段

  • cut 取的单位
    -b 以字节为单位
    -c 以字符为单位
    -f 以字段为单位(使用定界符)
    eg:

    cut -c1-5 file //打印第一到5个字符
    cut -c-2 file //打印前2个字符

wc 统计行和字符的工具

wc -l file // 统计行数
wc -w file // 统计单词数
wc -c file // 统计字符数

sed 文本替换利器

首处替换

seg 's/text/replace_text/' file   //替换每一行的第一处匹配的text

全局替换

seg 's/text/replace_text/g' file

默认替换后,输出替换后的内容,如果需要直接替换原文件,使用-i:

seg -i 's/text/repalce_text/g' file

移除空白行:

sed '/^$/d' file

变量转换,已匹配的字符串通过标记&来引用

echo this is en example | seg 's/\w+/[&]/g'
$>[this]  [is] [en] [example]

子串匹配标记
第一个匹配的括号内容使用标记1来引用

sed 's/hello([0-9])/1/'

双引号求值,sed通常用单引号来引用;也可使用双引号,使用双引号后,双引号会对表达式求值:

sed 's/$var/HLLOE/'

当使用双引号时,我们可以在sed样式和替换字符串中指定变量;

p=patten
r=replaced
echo "line con a patten" | sed "s/$p/$r/g"
$>line con a replaced

awk 数据流处理工具

awk脚本结构
awk ‘BEGIN{ statements } statements2 END{ statements }’

  • 工作方式
  1. 执行begin中语句块;
  2. 从文件或stdin中读入一行,然后执行statements2,重复这个过程,直到文件全部被读取完毕;
  3. 执行end语句块;

使用不带参数的print时,会打印当前行;

echo -e "line1nline2" | awk 'BEGIN{print "start"} {print } END{ print "End" }'

print 以逗号分割时,参数以空格定界;

echo | awk ' {var1 = "v1" ; var2 = "V2"; var3="v3"; 
print var1, var2 , var3; }'
$>v1 V2 v3

使用-拼接符的方式;

echo | awk ' {var1 = "v1" ; var2 = "V2"; var3="v3"; 
print var1"-"var2"-"var3; }'
$>v1-V2-v3

特殊变量:NR NF $0 $1 $2

NR:表示记录数量,在执行过程中对应当前行号;
NF:表示字段数量,在执行过程总对应当前行的字段数;
$0:这个变量包含执行过程中当前行的文本内容;
$1:第一个字段的文本内容;
$2:第二个字段的文本内容;

echo -e "line1 f2 f3n line2 n line 3" | awk '{print NR":"$0"-"$1"-"$2}'

打印每一行的第二和第三个字段:

awk '{print $2, $3}' file

统计文件的行数:

awk ' END {print NR}' file

累加每一行的第一个字段:

echo -e "1n 2n 3n 4n" | awk 'BEGIN{num = 0 ;
print "begin";} {sum += $1;} END {print "=="; print sum }'

传递外部变量

var=1000
echo | awk '{print vara}' vara=$var 
#  输入来自stdin
awk '{print vara}' vara=$var file 
# 输入来自文件

Spark学习记录

本笔记主要记录Spark在Hadoop环境中使用python语言进行算法开发过程中遇到的问题

Spark的启动

在Spark的安装路径中的bin路径中有pyspark,这是一种交互式的调用方法,还可以编写好python脚本之后通过

spark-submit xxx.py

的方式来调用。

sc is not defined

任何Spark程序都需要一个SparkContext来开始,其包含了Spark集群配置的各种参数,如果代码报错sc is not defined,需要添加如下代码进行初始化:

from pyspark import SparkContext
sc = SparkContext('local[2]', 'First Spark App')

常用HDFS Shell命令

大多数FS Shell命令和对应的Unix Shell命令类似,格式为hadoop fs ,如下:

hadoop fs -cat URI [URI …]
hadoop fs -chmod [-R] <MODE[,MODE]... | OCTALMODE> URI [URI …]
hadoop fs -cp URI [URI …] <dest>
hadoop fs -du URI [URI …]
hadoop fs -mkdir <paths>
hadoop fs -ls <args>
hadoop fs -rmr URI [URI …]    # rm -rf

在HDFS和本地之间复制文件的命令如下:

hadoop fs -get [-ignorecrc] [-crc] <src> <localdst>    # 复制文件到本地文件系统
hadoop fs -put <localsrc> <dst>    # 本地文件复制到HDFS

机器学习技巧总结

SVM vs Logistic Regression

  1. 逻辑回归和线性核SVM本质上其实没啥区别;
  2. 特征数大于样本数或者二者数量相当时,逻辑回归或者线性核SVM会有比较好的效果;
  3. 特征数较少,样本数一般多时,高斯核SVM会有比较好的效果;
  4. 特征少,样本特别多时,构建更多的特征,然后用逻辑回归或者线性核SVM

调参技巧

解决测试误差较大的办法

  1. 增加训练样本 -> 降低方差
  2. 减少特征数量 -> 降低方差
  3. 增加新特征 -> 降低偏差
  4. 现有特征多项式组合(x1^2, x2^2, x1*x2等等) -> 降低偏差
  5. 降低正则λ -> 降低偏差
  6. 增加正则λ -> 降低方差

python技巧总结

查看运行时间

用ipython的run命令做profiling,找出耗时语句

run -p xxx.py

ipynb文件

ipynb文件是用浏览器演示python交互的优良插件,可以提供运行代码,插图,文章说明,插入公式等多种功能。
只需在含有ipynb文件的的父目录位置执行

ipython notebook

即可生成shell服务器端,浏览器作为客户端展示出来。

内存优化

及时释放内存。大数组使用完之后,手动del释放array。注意所有对数组的引用都del之后,数组才会被del。这些引用包括A[2:]这样的view。
尽量重用内存,比如:

A = B + C # B is neverused later

可以改写成

B += C
A = B

py2exe

windows环境把python脚步打包成可执行程序,安装py2exe。注意使用pip install安装只能支持python3版本,要想在python2.7环境下使用py2exe,要在这里下载py2exe-0.6.9.win32-py2.7.exe

在脚步所在目录下新建一个setup.py文件,加入

from distutils.core import setup
import py2exe

setup(windows = ['xxxxxx.py'])

然后执行

python setup.py py2exe

然后可执行程序以及相关依赖都会创建在dist文件夹,打包带走dist即可。

random随机数模块

random.random

用于生成[0, 1.0)的随机浮点数

random.uniform(a, b)

生成[a, b]的随机浮点数

random.randint(a, b)

生成[a, b]的随机整数

random.choice(sequence)

从sequence中随机得到一个元素

random.shuffle(x[, random])

用于将列表x元素打乱顺序

random.sample(sequence, k)

从指定序列中随机获取长度k的片段,不改变原有序列