Quantcast
Channel: Sam的技术Blog
Viewing all 158 articles
Browse latest View live

OAL_Tengine学习Tengine_API

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

对Tengine API的学习记录。

0. Tengine API一些基本概念和TensorFlow中的对比:
Tengine API 体系中,graph, Node, Tensor等概念与Tensorflow中相同。

Graph 相当于一个计算任务。 通过读取模型文件,创建一个对应的graph.
Node: Graph中的一个节点,可以认为是一个Op. 可以获取0-N个Tensor, 执行计算,产生0-N个Tensor.
Tensor: 为一个N维度数字或者List。作为Op的 Input或者Output.
shape: Tengine的Shape和TensorFlow的Shape相同。但它更像是设置:NCHW/NHWC.


1. Tengine API :
1.1: 初始化,反初始化,版本控制部分:
rel = init_tengine();
if(rel != 0)
{
printf("Initialize Tengine error.");
return -1;
}



// 2. Tengine Version
pVersion = get_tengine_version();
printf("\nTengine Version is: [%s]\n", pVersion);


    if(request_tengine_version("0.9") < 0)
{
printf("\nrequest_tengine_version is 0.9. \n");
        return -1;
}

release_tengine();
讲解:
使用Tengine库是,首先要初始化,这个过程需要一点时间。后续是版本相关函数。

1.2:Graph相关:
1.2.1:创建Graph:
    PNet_graph = create_graph(NULL, "tengine", "models/p.tftmfile");
    if(PNet_graph == NULL)
    {
        std::cout << "Sam Info:Create Pnet Graph failed\n";
        std::cout << "errno: " << get_tengine_errno() << "\n";
        return -1;
    }
    std::cout << "SamInfo: Create PNet Graph success." << std::endl;

1.2.2:销毁Graph:
destroy_graph(PNet_graph);

一个Graph,就是一个计算任务,Tengine载入模型(caffe, Tensorflow,Tengine). 以此作为一个计算任务。

1.2.3: 预运行图:
int prerun_graph(graph_t graph)
在真正运行图之前,需要预先准备好资源。 只用调用一次。

1.2.4: 运行图:
int run_graph(graph_t graph, int block)
执行graph计算任务。参数二: 是否阻塞。


1.2.5:释放运行图所用到的资源:
int postrun_graph(graph_t graph)


1.3: Tensor相关 API:
1.3.1: 通过名字获取Graph 中指定Tensor:
tensor_t get_graph_tensor(graph_t graph, const char* tensor_name)

1.3.2:获取和设置Tensor Shape:
int set_tensor_shape(tensor_t tensor, const int dims[], int dim_number)
int get_tensor_shape(tensor_t tensor, int dims[], int dim_number)
获取和设置指定Tensor的shape.

1.3.3:获取Tensor Buffer size:
int get_tensor_buffer_size(tensor_t tensor)

1.3.4:获取和设置Tensor Buffer:
void* get_tensor_buffer(tensor_t tensor)
int set_tensor_buffer(tensor_t tensor, void* buffer, int buffer_size)

总结一:
以上API,已经能用最简单的方式完成一个推理。
A. 初始化Tengine,载入模型,创建Graph. 
B. 通过Tensor 名字,获取Graph的输入Tensor和输出Tensor句柄。
C. 设置shape后,设置输入Tensor的Buffer。 并分配对应空间。 
D. 把数据归一化后, 放置到buffer空间。
E. run graph.
F. 通过Tensor名,获取Graph的输出Tensor。
G. 获取Tensor的输出Buffer.
H. 解析Buffer。
.
.
Z. 释放Graph资源。  Release Graph。反初始化Tengine.


2. 其它相关Tengine API:
2.1 Node相关:
2.1.1:获取Graph Input Node 数量:
int get_graph_input_node_number(graph_t graph)

2.1.2:获取Graph Output Node数量:
int get_graph_output_node_number(graph_t graph)

2.1.3: 获取指定Graph的第index个Input Node的句柄:
node_t get_graph_input_node(graph_t graph, int idx)

2.1.4:  获取指定Graph的第index个Output Node的句柄:
node_t get_graph_output_node(graph_t graph, int idx)

2.1.5: 获取指定Graph中所有Node数量:
int get_graph_node_number(graph_t graph)

2.1.6: 获取指定Graph的第index个Node句柄:
node_t get_graph_node_by_idx(graph_t graph, int node_idx)

通过以上几个函数,可以获取Graph的 Input/output Node数量。再通过get_graph_input_node(), get_graph_output_node()获取每个Node Handle.

2.1.7:获取Node名:
const char* get_node_name(node_t node)

pNode_Name = get_node_name(node);
std::cout << "\t Node Name is:" << pNode_Name << std::endl;
2.1.8:获取Node Op:
const char* get_node_op(node_t node)

pNode_Op = get_node_op(node);
std::cout << "\t Node Op is:" << pNode_Op << std::endl;


2.2: Tensor相关 API: 
2.2.1: 获取指定Node的Input Tensor数量:
int get_node_input_number(node_t node);

2.2.2:获取指定Node的Output Tensor数量:
int get_node_output_number(node_t node);

2.2.3:获取Node的指定Index Input Tensor句柄
tensor_t get_node_input_tensor(node_t node, int input_idx);

2.2.4:获取Node的指定Index Output Tensor句柄
tensor_t get_node_output_tensor(node_t node, int output_idx);

通过以上组合,即可得到Graph 所有Input/Output Tensor Handle.






 

Tensorflow基础学习CNN卷积神经网络

$
0
0
作者: Sam (甄峰) sam_code@hotmail.com

0. 传统神经网络存在的问题:
全连接深度神经网络,顾名思义,就是每个神经元都与相邻层的每个神经元相连接。
以图片为例:
有一组100x100的图片要训练。则输入层拥有100x100=10000个神经元。假设只有一个隐藏层,也是10000个神经元。则W1的shape为[10000, 10000]. 有10000x10000个权值要确认。

我们知道,数据量(样本数)要和网络复杂度相匹配。 
数据少,但网络太复杂,容易过拟合。
数据过多,网络太简单,则容易欠拟合。 
一些人说:数据量要相当于未知权值的5-30倍。 

那10000x10000个未知权值,需要的数据量太大,且计算量也太大。

1. 卷积神经网络的三个基本思想:
A. 局部感受野(Local receptive fields)
B. 权值共享。
C. 池化(pooling)

1.1: 局部感受野:
1962年,哈佛医学眼的几位教授在研究猫眼神经时。发现目标在猫的眼睛不同区域移动时,猫眼神经对应区域会活跃。提出局部感受野概念。
在全连接神经网络中,把输入层的每个神经元都与第一隐藏层的每个神经元相连接。
在CNN中,第一隐藏层的神经元,只与局部区域输入层的神经元相连接。这个局部区域即为局部感受野
可以认为,隐藏层的神经元,学会了只分析它的视野范围内的特征。

举例说明:
MNIST的每张图片大小为28x28. 每个数字代表一个像素。则CNN的输入层有28x28个神经元,第一隐藏层中,每个神经元只与5x5个输入层神经元相连接。隐藏层的每个神经元,都有一个5x5的权值与之对应。

1.2:权值共享:
隐藏层的神经元,权值和偏移值是共享的。
所以可以这样看待:每个权值(例如:5x5)像一个滑动窗, 依次从输入层滑过,把获取的值填入后面的层.
这个滑动窗提测(detect)到的是输入层同一种特征(feature). 在滑动中,把整个输入层的特征记录到后面的隐藏层中了。

一个窗口只能学到一种特征
窗口还可以称为: 卷积核(Kernel),  过滤器(filter).

在图像识别中,光学习一个特征,肯定不够。要学习多个特征,就要有更多的卷积核(kernel)。
卷积操作时,使用不同的卷积核,能得到不同的特征图,多个特征图对分类有极大提升。
例如:可以使用32个卷积核,生成32份特征。 窗口和窗口之间的W和b不是共享的。 32个窗口,就表示有32个W(5x5)矩阵和B。

权值共享可以极大地减少模型参数个数。

1.3:池化(pooling):
池化层通常接在卷积层后面。 池化的目的在于简化卷积层的输出。

例如:pooling窗口为2x2. 采用max pooling. 在卷积层后,每2x2个数据,去除最大值作为池化层的一个数据。

常用的池化有三种:
A: max pooling, 取窗口中最大值。 
B: mean pooling, 取平均值。
C: 随机 pooling.   随机取某一个。

2. 卷积和池化时的padding方式:
卷积的Padding方式:
A. SAME  Padding:
给输入层的周边补0,让特征图的大小与采样图的大小相同。
B. VALID Padding:
输入层的周边不会补0,卷积核不会超出采样图边界。 
所以特征图的大小为: N-n+1.  
N: 采样图长度。 
n: 卷积核长度。

池化的Padding:
池化的Padding与卷积的Padding分类相同,但涵义不同。
A. SAME Padding:
可能会补0. 当采样图的大小不够时,会补0.
B. VALID  Padding:
绝对不会补0. 

3. Data_format:
所谓Data_format,其实是指4维数组的排列方式。它定义了一批图片数据的存储顺序。分NCHW, NHWC两种。Tensorflow中,默认的是NHWC.
N:  Batch数。
H:  Height
W: Width
C:  Channel数目。灰度图:1.  RGB:3

这个问题的实质是:将一张二维张量(h*w)拼接成三维(w*h*c)时,有两种做法:
NHWC: [batch, height, width, channels]
NCHW: [batch, channels, height, width]

例如:
NCHW: [batch, channels, height, width]
[4, 3, 128, 128]: 每份数据为4张图,RGB, 128x128.
[[[R:128x128],[G:128x128],[B:128x128]],
[[R:128x128],[G:128x128],[B:128x128]],
[[R:128x128],[G:128x128],[B:128x128]]
[[R:128x128],[G:128x128],[B:128x128]]]
共有4分,每份中分三个数据块,每个数据块是一个channel的数据。(RGB各个色度的数据集中存放)

NHWC:[batch, height,width, channels]
[4, 128, 128, 3]:每份数据为4张图, 128x128, RGB
[[RGB]....128x128个,
[RGB]....128x128个,
[RGB]....128x128个,
[RGB]....128x128个,
]


4. Tensorflow 中 和CNN相关的几个几个接口:
4.1:卷积:
tf.nn.conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=True,
    data_format='NHWC',dilations=[1, 1, 1, 1],
    name=None,
)
使用4-D输入 input(样本), filter(卷积核), 计算一个2-D卷积。

参数讲解:
data_format: 一个string. 有两个选项:"NHWC"/"NCHW". 用来指定输入--input(样本)和输出的数据格式。
input: 一个tensor. 作为输入。 数据类型必须为:half, bfloat16, float32, float64. 它是个4-D Tensor. 维度顺序由data_format决定。
filter: 卷积核。是个4-D Tensor,数据类型与input相同。[filter_height, filter_width, in_channels, out_channels]
strides: 卷积核滑动步长。一个List, 长度为4. 在每个维度上的滑动步长。维度顺序由data_format决定。如果为NHWC,则[1,n,n,1]. 0和3位置一定为1.
padding: 一个string. 卷积的padding模式。“VALID”/ "SAME"

4.2: 池化(Pooling)
tf.nn.max_pool(
    value,
    ksize,
    strides,
    padding,
    data_format='NHWC',
    name=None,
)
在input(value)上执行max pooling.
参数讲解:
value: 一个4-D Tensor. 格式由data_format决定。它将用来被池化。
ksize: 一个List或数组。4个int值。 用来描述各个维度上的池化核的大小.[1, n, n, 1]. 0,3位一定为1.
strides: 一个List或数组。4个int值。 描述每个维度的滑动步长。[1,n,n, 1]
padding: 池化 padding模式。 “VALID”/ "SAME"
data_format: 一个string. 有两个选项:"NHWC"/"NCHW".


5. CNN卷积,池化的实际使用
5.1:创建权值和偏移量:
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)

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

要创建的权值和偏移量,都必须是个Variable. 才可以被训练。
偏移量,可以设定为0.1
权值,则采用正则分布的值。

tf.truncated_normal(
    shape,
    mean=0.0,
    stddev=1.0,
    dtype=tf.float32,
    seed=None,
    name=None,
)
输出random的值,为一段正则分布数据。
shape: shape.
mean:分布的中间值。
stddev:标准偏移量。
seed:种子。

5.2:Tensor的重新设置shape:
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])

x_image = tf.reshape(x, [-1, 28,28,1])
可以看到,x本来分配了一个Nx784的placeholder. 可现在需要把这个2-D Tensor转成 4-D Tensor。
可以使用reshape. 但数据一定要能够对上。 比如None份,就是batch. 
28x28=784. 

5.3:以MNIST为例,查看CNN训练过程
5.3.1:第一层卷积
# 5x5. 1 ch.  32 conv kernel=32 output
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
首先创建第一层隐藏层的权值和偏移量。
卷积核:5x5. 
卷积核个数:32个。 则输出32个特征图。

池化
#h_conv1. [100, 28,28,32]
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
#h_pool1: [100, 14, 14, 32]
h_pool1 = max_pool_2x2(h_conv1)

5.3.2:第二层卷积:
第一层卷积池化后,有32张特征图,每张图大小14x14.

第二层卷积层,卷积核大小为:5x5, 第一层卷积层的输出是32个特征图,此处作为输入,输出则为64个特征图
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])

#h_conv2 [100, 14,14,64]
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
#h_pool2 [100, 7, 7, 64]
h_pool2 = max_pool_2x2(h_conv2)
第二层卷积池化后, 每份数据有64张特征图, 每张图7x7 大小。



两个全连接层,一个神经元个数为1000,另一个为输出层,神经元个数为10.
#全连接
W_fc1 = weight_variable([7*7*64, 1024])# 第一个全连接层, 1024个神经元
b_fc1 = bias_variable([1024])

#
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])

h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

#dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)

#交叉熵代价函数
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=prediction))

train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)


 

Tensorflow基础学习模型相关

$
0
0
作者: Sam (甄峰) sam_code@hotmail.com

谈到模型,先要明确我们在训练后,希望存储的是什么? 

 

机器学习基础概念

$
0
0
作者: Sam (甄峰) sam_code@hotmail.com

学习算法已经深入到各类服务中,如Google,baidu的网页排序。垃圾邮件的过滤,购物app的推荐系统,都使用了learning algorithm.

机器学习,就是让AI模拟人类大脑的学习方法。是机器学习的一个重要方向。
应用场景包括:数据挖掘(Database mining), 自然语言的处理(NLP),计算机视觉(CV)等。

机器学习(Machine Learning)的定义:
Arthur Samuel(1959)对Machine Learning的定义: 在无明确设置的情况下,使计算机具有学习能力的研究领域。 (Arthur Samuel 编写了一个跳棋算法,让他们自己对弈,实现了棋力的提升)
Tom Michell(1998)对Machine Learning 的定义:计算机程序从经验E中学习,解决某一个任务T, 进行某一个性能度量P。 通过P测定在T上的表现因经验E而提升。

Machine Learning Algorithms:
Supervised Learning,监督学习。
Unsupervised Learning, 非监督学习。
强化学习
推荐系统。


监督学习(Supervised Learning)概念
我们给算法一个数据集,其中包括正确答案,它根据这些数据,学习出规律。以做出预测。
其中分:
回归问题(Regression), 用来预测连续的数值输出。
分类问题(Classification), 用来预测离散值的输出。
也有人这样陈述,Sam觉得有道理:
回归的输出是连续的,比如:1、2、3、4、5、6。注意,所谓“连续”意味着是有序的,是排序的。比如输出为3,那么我们可以肯定真实为3、4、5、6的可能性顺序减小,真实为2、1的可能性也是顺序减小。

分类的输出是:A类、B类、C类。注意,所谓“分类”意味着ABC之间不存在排序,不存在谁比谁更亲密或更远、可能或更不可能。输出为A,那么不意味着真实为B的可能性比C更大






非监督学习概念
非监督学习是指,告诉算法,这里有一些数据,你能找到其中的结构么?

其中包括聚类算法,鸡尾酒会算法等。

聚类算法: 把给定的数据集分成不同的簇。 例如:Google News会自动搜索网络上的新闻,并把他们分簇,同一主题的新闻放在一起。

鸡尾酒会算法
在混杂的鸡尾酒会现场,有无数人同时说话,有很多麦克风录取声音数据。 用鸡尾酒会算法可以区分出不同人的声音。



 

机器学习代价函数

$
0
0
作者: Sam (甄峰) sam_code@hotmail.com

1:基础知识
1.1:  假设函数
  

一元线性回归(单变量线性回归)
通过训练,推导出假设函数。即: Training Set 通过Learning Algorithm,获取Hypothesis.
假设函数,x作为输入, 输出是预测值。  
θ0 和θ1 :Parameters. 模型参数。 参数的改变,导致假设函数的变化,进而导致输入值的变化。





1.2: 损失函数(Loss Function):
是定义在单个样本上的,算的是一个样本的误差。



1.3. 代价函数(Cost Function):
是定义在整个训练集合(Training Set)上的,是所有样本误差的平均。也就是损失函数的平均。


2. 代价函数(Cost Function): 
在现实例子中,数据会以点的形式提供,想要解决回归问题,就要将这些点拟合成一条直线,找到最优的θ0 和θ,让这条直线能够最好的代表实际情况。

如果才算是最好的情况呢? 当然是取适合的θ0 和θ后,让所有假设函数计算出的值与真实值差距最小。这个差距值,即Cost Function的结果。

Cost Function可以表示为:J(θ0 , θ)
机器学习中代价函数有很多,根据不同的问题,选用不同的代价函数,比如逻辑回归使用对数代价函数,分类问题使用交叉熵作为代价函数。 平方误差代价函数则很常用,因为误差平方代价函数,对于大多数问题,特别是回归问题,都是一个合理的选择。 

2.1:平方误差代价函数:
为了使这个值不受个别极端数据影响而产生巨大波动,采用类似方差再取二分之一的方式来减小个别数据的影响。所以就产生了以下公式:


m: 样本个数。


在线性回归中,我们需要寻找合适的参数(θ0 和θ1), 而让输出值和真实值差距最小。 所以,我们要解决的是一个最小化问题。即求出代价函数的最小值。


2.2:二次代价函数数学涵义:
我们先简化一下,θ0 和θ1简化到一个变量θ1:





如果是两个单数:θ0 和θ1


我们需要求出代价函数的最小值。



3. 梯度下降法:
梯度下降法是一种寻找目标函数最小化的方法。不光用于线性回归上,还被广泛应用于机器学习的众多领域。

3.1:梯度:
梯度是衡量一个函数特性----稍微改变输入,输出的改变率。
也就是函数的斜率。也就是微积分中的导数。

3.2:步骤:
A. 给定θ0 , θ1初始值。
B. 不断改变θ0 , θ1,让J(θ0 , θ)不断变小。直到找到最小值或者局部最小值。

3.3:数学原理:
代价函数:为J(θ0 , θ):

重复做以下动作,直到收敛。
\theta_i = \theta_i - \alpha\frac{\partial}{ \partial\theta_i}J(\theta)
其中: α为学习率。可以看做步长。
后面跟的其实是导数。
注意: θ0 , θ1要同步更新。

3.4:学习率:
学习率太大太小都不好。








梯度下降不一定能找到全局最优解,有可能是一个局部最优解。起始点不同时,可能到达的局部最小值也不同。

当损失函数是凸函数的时候,梯度下降法所求的解就是全局最优解。


 

线性回归代价函数是凸函数,无局部最优解,只有一个最优解。所以用梯度下降法很适合。





 

Camera常用格式MJPEG和jpeg-turbo库

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

USB Camera在通过V4L2接口获取Camera数据时,有多种图像格式可选择。但最常用的却是YUV和MJPEG. 又因为YUV图片所占空间大,在超过640x480情况下,会超过USB数据传输带宽。 所以只能使用MJPEG. 

现在就谈谈MJPEG相关内容。

1. 视频压缩格式:
1.1:JPEG (Joint Photographic Experts Group)压缩技术是所有图像压缩技术的基础。 它适合静态图像的压缩,直接处理整幅画面,压缩倍数为20-80倍,分辨率没有选择的余地。在传输中,必须要等整个压缩档传输完成才可以解压,这就造成传输时间较长。它是帧内压缩。


1.2. MJPEG(Motion JPEG):
在JPEG基础上发展起来的动态图像压缩技术,也是帧内压缩,只单独的对某一帧进行压缩,而基本不考虑视频流中不同帧之间的变化。其压缩后的图像还可以任意剪切。
Motion JPEG, 即动态JPEG. 按一定帧数使用JPEG算法压缩视频信号,完成动态视频的压缩。MJPEG图像流的单元就是一帧一帧的JPEG画片。 因为每帧可以任意存取,所以MJPEG常用于视频编辑系统。

1.3. MPEG:
其实是一个标准。
其中MPEG-4. 它会使用帧间数据。压缩比很高。应用广泛。


2. libjpeg-turbo:
jpeg, Mjpeg的压缩和解压,比较耗费CPU。 所以就有了libjpeg-turbo库,它可以更快的压缩和解压jpeg数据。

因为是帧内压缩,所以可以对JPEG数据进行各种处理,如放大/缩小,翻转,剪切,镜像等。

2.1:  libjpeg-turbo 对JPEG的压缩和解压:
libjpeg-turbo提供压缩(Compress)和解压(Decompress)接口。
同时,它还有个概念----Transform.

2.1.1: 压缩:
给定RGB data. 把它压缩成指定jpeg文件。
A. 打开一个压缩实例:
 tjhandle tjInitCompress(void)

B. 把给定pixel,w,h pitch等信息的数据,压缩成jpeg(给定sample,质量)数据。
int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
                          int width, int pitch, int height, int pixelFormat,
                          unsigned char **jpegBuf, unsigned long *jpegSize,
                          int jpegSubsamp, int jpegQual, int flags)

C.  销毁实例:
int tjDestroy(tjhandle handle)


2.1.2:解压:
把给定的JPEG数据,解压成RGB数据:
这个过程,只是简单的解压,或者仅仅是图片大小变化。

A. 打开一个解码实例
tjInstance = tjInitDecompress();

B. 通过读取到的jpeg数据,来分析文件头,获取图像信息(长宽,sample, colour信息等)
int tjDecompressHeader3(tjhandle handle,
                                  const unsigned char *jpegBuf,
                                  unsigned long jpegSize, int *width,
                                  int *height, int *jpegSubsamp,
                                  int *jpegColorspace)
jpegBuf是jpeg数据。

C. 解压到指定格式,指定长宽数据:
int tjDecompress2(tjhandle handle, const unsigned char *jpegBuf,
                            unsigned long jpegSize, unsigned char *dstBuf,
                            int width, int pitch, int height, int pixelFormat,
                            int flags)
width, height: 目标图像长宽。
pixelFormat: 目标图像格式, enum TJPF 中定义,如RGBX。

此时,dstBuf中的内容,就是解压过的,指定大小,深度,和图像格式的图片数据了。

如果指定为RGBX,则直接存文件即为BMP文件。

D. 销毁实例:
 int tjDestroy(tjhandle handle)

2.1.3:Transform:
Transform是干什么呢,Sam觉得是因为要实质修改JPEG数据,重新排列。 比如剪切,翻转,镜像。这些动作叫做Transform.

A. 打开一个transform实例:
tjhandle tjInitTransform(void)


B. Transform转换:

 tjtransform xform;  //指定transform的参数,如裁剪区域
想要裁剪:
xform.r.x = iX; //左上角位置
xform.r.y = iY;
xform.r.h , xform.r.w  //区域高度。
xform.options |= TJXOPT_CROP;   //指定要剪切。


int tjTransform(tjhandle handle, const unsigned char *jpegBuf,
                          unsigned long jpegSize, int n,
                          unsigned char **dstBufs, unsigned long *dstSizes,
                          tjtransform *t, int flags)

dstBufs中保存这transform后的数据。

C. 可选: 获取新的jpeg数据信息:
DLLEXPORT int tjDecompressHeader3(tjhandle handle,
                                  const unsigned char *jpegBuf,
                                  unsigned long jpegSize, int *width,
                                  int *height, int *jpegSubsamp,
                                  int *jpegColorspace)

D. 后续可以随意处理jpeg数据了。如解压。
E. 销毁:
int tjDestroy(tjhandle handle)



2.2: libjpeg-turbo对图像文件的处理:
unsigned char *tjLoadImage(const char *filename, int *width,
                                     int align, int *height, int *pixelFormat,
                                     int flags)


int tjSaveImage(const char *filename, unsigned char *buffer,
                          int width, int pitch, int height, int pixelFormat,
                          int flags)



2.3:获取放大缩小factor:
libjpet-turbo并不可以随意比例放大缩小,而是指定了一系列(16个)比例系数。
可以先获取这个列表:

scalingFactors = tjGetScalingFactors(&numScalingFactors);
if(scalingFactors == NULL)
{
printf("\nGet Scaling Factor Error.\n");
return -1;
}
printf("\nThere are [%d] ScalingFactors\n", numScalingFactors);
for(int i = 0; i < numScalingFactors; i++)
{
printf("\t Numerator/Denominator is:[%d/%d]\n", scalingFactors[i].num, scalingFactors[i].denom);
}

此时就可以得到turbo-jpeg给定的Scaling Factor.










 

SELinux和在Android下应用初探

$
0
0
本文大量摘抄。

SELinux(Security-Enhanced Linux)安全增强Linux。 它是Linux Kernel的一个子系统。2.6 及以上版本Kernel都已经集成了SELinux.

1. Linux下权限管理背景:
1.1: DAC:
在早期Linux 版本中,决定一个资源是否能够被访问,是由这个用户或组对这个资源是否有对应权限(r, w, x)决定的。只要访问这个资源的的进程用户或者组符合条件,就可以正常访问。
而资源对root用户不设管制,root用户对系统中任何资源都可以无限制访问。
这种权限管理机制的主题是用户,称为自主访问管理(DAC)
1.2:MAC:
由于DAC风险太大,若一个服务有漏洞,而它是root用户运行的,那攻击者可以通过它为所欲为。所有有了MAC。
决定一个资源是否能被访问的因素除了资源本身的r,w,x设置之外,还需要判断每一类进程是否拥有对某一类资源有访问权限。 此时,当某个服务用root身份运行,也需要判断这个进程的类型以允许访问的资源类型,才能决定它是否能够访问此资源。 这种权限管理机制的主题是进程,称为强制访问管理(MAC) (是不是感觉有点像Android APP的资源权限)
MAC又分 类别安全(MCS)模式和多级安全(MLS)模式。

2. SELinux设计目标和基本概念:
SELinux的主要目的就是最大限度的减小系统中服务进程可访问的资源(资源最小化原则)。

A:主体(Subject):
等同于进程。
B: 对象(Object):
被访问的资源。可以是文件,目录,端口,设备等。
C: 政策和规则(Policy & Rule)

系统中通常有大量的文件和进程,为了节省时间和开销,通常我们只是选择性地对某些进程进行管制。

而哪些进程需要管制、要怎么管制是由政策决定的。

一套政策里面有多个规则。部分规则可以按照需求启用或禁用(以下把该类型的规则称为布尔型规则)。

规则是模块化、可扩展的。在安装新的应用程序时,应用程序可通过添加新的模块来添加规则。用户也可以手动地增减规则。

在 CentOS 7 系统中,有三套政策,分别是:

1. targeted:对大部分网络服务进程进行管制。这是系统默认使用的政策(下文均使用此政策)。

2. minimum:以 targeted 为基础,仅对选定的网络服务进程进行管制。一般不用。

3. mls:多级安全保护。对所有的进程进行管制。这是最严格的政策,配置难度非常大。一般不用,除非对安全性有极高的要求。

政策可以在 /etc/selinux/config 中设定。

D: 安全上下文(Security Context)

安全上下文是 SELinux 的核心

安全上下文我自己把它分为「进程安全上下文」和「文件安全上下文」。

一个「进程安全上下文」一般对应多个「文件安全上下文」。

只有两者的安全上下文对应上了,进程才能访问文件。它们的对应关系由政策中的规则决定。

文件安全上下文由文件创建的位置和创建文件的进程所决定。而且系统有一套默认值,用户也可以对默认值进行设定。

需要注意的是,单纯的移动文件操作并不会改变文件的安全上下文。

安全上下文的结构及含义

安全上下文有四个字段,分别用冒号隔开。形如:system_u:object_r:admin_home_t:s0。









 

机器学习线性回归一

$
0
0
作者: Sam (甄峰) sam_code@hotmail.com

监督学习下的模型是什么:

x: 输入值或者特征。
y: 输出值或者目标变量。
(x, y):一个数据集。



给定一个训练集(Training Set), 通过学习算法(Learning Algorithm). 获取一个假设函数(Hypothesis). 
这个假设函数就是我们训练的成果。 可以给它输入 x(输入变量), 输出一个y(输出变量)

假设函数(Hypothesis)是一个引导从x 到y 函数。

线性回归:


一元线性回归(univariate linear regression):
也叫作:linear regression with one variable.
当输入变量(属性)只有一个时,把它叫做一元线性回归模型。

为了能够让h(x)所代表的直线能够更好的拟合真实数据,需要找到最好的theta0和theta1. 而什么是最好的参数--theta0和theta1. 就是要让取一组theta0,theta1, 让h(x)与y 整体之间差距最小。换句话说,就是让代价函数最小。(见

机器学习<二>代价函数

 )






以上就是线性回归模型+二次代价函数

如果采用梯度下降算法来最小化二次代价函数。 则称这个算法为线性回归算法。





如何梯度下降 Cost function J()呢。



例子代码实现:

import numpy as np
import matplotlib.pyplot as plt

#模拟一份线性变化的数据。400个离散点。
# x_data: 400x1 Matrix. y_data: 400x1 Matrix
# 它们的对应项,确定了点的x 和 y 值
x_data = np.linspace(-1.8, 1.8, 400)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape)
bias = 4
y_data = x_data * 3 + bias + noise  





#迭代次数
epochs = 100
#epochs = 1
#学习率
lr = 0.1

#theta给定初始值
Init_theta = [0, 0]



# 假设函数
#输入 theta(theta0, theta1), 和 某一个x 点的x 坐标值
#输出 推导出的y 坐标
def hypothesis(theta, x_dat): 
    h = theta[0] + theta[1]*x_dat
    return h
    
#二次代价函数
#输入 theta(theta0, theta1)
#输出 按这个theta算出的误差(代价)值
def Cost_J(theta): 
    dis = 0
    for x, y in zip(x_data, y_data):
        dis += (hypothesis(theta, x) - y)**2
    dis = dis/(2*len(x_data)) 
    return dis


#存储Cost function 结果
J_list = [Cost_J(Init_theta)]


#梯度下降法更新theta
#输入:theta(theta0, theta1) 初始值
#training set 的 X坐标集和Y坐标集
#输出:训练后的theta
def update_theta(theta, X_dat, Y_dat):
    up_theta = theta.copy()
    for i in range(epochs):
        theta0_grade = 0
        theta1_grade = 0
        for j in range(len(X_dat)):
            theta0_grade += hypothesis(up_theta, X_dat[j]) - Y_dat[j]
            theta1_grade += (hypothesis(up_theta, X_dat[j]) - Y_dat[j]) * X_dat[j]
            
        up_theta[0] = up_theta[0] - lr * theta0_grade / len(X_dat)
        up_theta[1] = up_theta[1] - lr * theta1_grade / len(X_dat)
        J_list.append(Cost_J(up_theta))
        #print(up_theta)
    return up_theta






print("Starting theta0 = {0}, theta1 = {1}, error = {2}".format(Init_theta[0], Init_theta[1], Cost_J(Init_theta)))
print("Running...")
Init_theta = update_theta(Init_theta, x_data, y_data)
print("After theta0 = {0}, theta1 = {1}, error = {2}".format(Init_theta[0], Init_theta[1], Cost_J(Init_theta)))



plt.figure()
plt.scatter(x_data,y_data)

plt.plot(x_data, Init_theta[0] + Init_theta[1] * x_data, 'r')



plt.figure()
plt.plot(J_list)


plt.show()




结果如下:
可以看到,其实40次就收敛了。

Starting theta0 = 0, theta1 = 0, error = [12.88777069]
Running...
After theta0 = [3.99991926], theta1 = [3.00057397], error = [0.00133854]


 

机器学习梯度下降法

$
0
0
作者: Sam(甄峰) sam_code@hotmail.com

 梯度下降法:
梯度下降法是一种寻找目标函数最小化的常用算法。不光用于线性回归上,还被广泛应用于机器学习的众多领域。




3.1:梯度:
梯度是衡量一个函数特性----稍微改变输入,输出的改变率。
也就是函数的斜率。也就是微积分中的导数。

3.2:步骤:
A. 给定θ0 , θ1初始值。
B. 不断改变θ0 , θ1,让J(θ0 , θ)不断变小。直到找到最小值或者局部最小值。(theta0和theta1要同时变化)




3.3:数学原理:
代价函数:为J(θ0 , θ):

重复做以下动作,直到收敛。

其中: α为学习率。可以看做步长。
后面跟的其实是导数。
注意: θ0 , θ1要同步更新。

3.4:学习率:
学习率太大太小都不好。
机器学习<二>代价函数

机器学习<二>代价函数





梯度下降不一定能找到全局最优解,有可能是一个局部最优解。起始点不同时,可能到达的局部最小值也不同。

当损失函数是凸函数的时候,梯度下降法所求的解就是全局最优解。
机器学习<二>代价函数

 

线性回归代价函数是凸函数,无局部最优解,只有一个最优解。所以用梯度下降法很适合。
机器学习<二>代价函数


 

机器学习线性回归二

$
0
0
作者: Sam(甄峰) sam_code@hotmail.com

多元线性回归模型(Linear Regression with multiple variable/ Multivariate Linear Regression):
当我们用来预测的特征有多个时,我们用以下关键词记录:



m: 训练集中数据份数。
n:  每份训练集数据中,包含的特诊数

假设函数(Hypothesis):


为了表示方便,我们在数据集中添加一列: x0.  且x0=1
换句话说:增加了第0个特征,它的值永远为1.


theta是一个n+1的 Vector, theta的转置向量则是一个Matrix.
x 是一个n+1 的Vector。


这些内容,和单变量线性回归类似。如果要编程实现,就要找出实现 代价函数J()偏导的方法:
梯度下降法应用到多元线性回归二次代价函数:


这些内容,和单变量线性回归类似。如果要编程实现,就要找出实现 代价函数J()偏导的方法:


因为x0 = 1. 所以这个公式其实和一元线性回归梯度下降公式统一。


例子:
先选择数据集:
波士顿房价数据集(Boston House Price Dataset)是回归中常用到的数据集。

数据集包含 506 个观察,13 个输入变量和1个输出变量

CRIM:城镇人均犯罪率。

ZN:住宅用地超过 25000 sq.ft. 的比例。

INDUS:城镇非零售商用土地的比例。

CHAS:查理斯河空变量(如果边界是河流,则为1;否则为0)。

NOX:一氧化氮浓度。

RM:住宅平均房间数。

AGE:1940 年之前建成的自用房屋比例。

DIS:到波士顿五个中心区域的加权距离。

RAD:辐射性公路的接近指数。

TAX:每 10000 美元的全值财产税率。

PTRATIO:城镇师生比例。

B:1000(Bk-0.63)^ 2,其中 Bk 指代城镇中黑人的比例。

LSTAT:人口中地位低下者的比例。

MEDV:自住房的平均房价,以千美元计。



这个训练集很多人用过了,应该很完备,就不再检验数据可靠性了。





 

机器学习六LogisticRegression

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

Logistic Regression虽然名为Regression(回归), 其实是个分类(classification)算法。

在大部分分类需求中,如果采用Linear Regression的方式,拟合出一条线,使用输出的特征,建立门限这个方法。会出现一些不好的情况。并不是一个好办法。 

而Logistic Regression作为常用的classification算法,其输出值处于0-1之间。 可以这样理解:Logistic算法的输出,就是分类到正分类的可能性百分数。



 

机器学习Logistic_Regression实例

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

实例如下:

import numpy as np
import matplotlib.pyplot as plt


data_set = np.loadtxt('Logistic_Data/testSet.txt',dtype='float')
print("house_data shape is:", data_set.shape)
#print(data_set[:3])


#迭代次数
epochs = 10000
#epochs = 1
#学习率
lr = 0.01


X_Data = data_set[:, 0:-1]
Y_Data = data_set[:, -1]
#print("X_Data type is:",type(X_Data))
#print("X_Data shape is:",X_Data.shape)
#print(X_Data[:3])
#print(type(Y_Data))
#print("Y_Data shape is:",Y_Data.shape)
#print(Y_Data[:3])
X_Data = np.insert(X_Data, 0, 1, axis=1)
#print(X_Data[:3])


Test_Number = int(len(X_Data) * 0.75)
X_Training = X_Data[:Test_Number]
Y_Training = Y_Data[:Test_Number]
X_Test = X_Data[Test_Number:]
Y_Test = Y_Data[Test_Number:]


Init_theta = [1] * X_Training.shape[1]


def sigmoid(z):
    return 1 / (1 + np.exp(-z))




def hypothesis(theta, x_dat): 
    medv = map(lambda x,y:x*y, theta, x_dat)
    s_dat = sum(medv)
    return sigmoid(s_dat)

def loss_funtion(X_dat, Y_dat, theta):
    m, n = np.shape(X_dat)
#    m = X_dat.shape[0]
    loss = 0.0
    for i in range(m):
        sum_theta_x = 0.0
        
        sum_theta_x += hypothesis(theta, X_dat[i])
        
        loss += -Y_dat[i] * np.log(sum_theta_x) - (1 - Y_dat[i]) * np.log(1 - sum_theta_x)
    return loss

def update_theta(theta, X_dat, Y_dat):
    up_theta = theta.copy()
    for i in range(len(up_theta)):
        temp = 0
        for X_row,y in zip(X_dat,Y_dat):
            temp += (hypothesis(theta,X_row) - y) * X_row[i]
        temp /= len(X_dat)
        up_theta[i] = theta[i] - (lr*temp)
        
    return up_theta






def plotloss(loss_array):
    n = len(loss_array)
    plt.xlabel("iteration num")
    plt.ylabel("loss")
    plt.scatter(range(1, n+1), loss_array)
    plt.show()




J_list = [loss_funtion(X_Training, Y_Training, Init_theta)]
for i in range(epochs):
    theta_new = update_theta(Init_theta, X_Training, Y_Training)
    J_list.append(loss_funtion(X_Training, Y_Training,theta_new))
    #print("%d" % i)
    #print(loss_funtion(X_Training, Y_Training, theta_new))
    
    Init_theta = theta_new
     

print(J_list[-1])
plotloss(J_list)





 

机器学习Neural_Network一

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

0. Neural Network算法来历:
神经网络是一个古老算法。它沉寂过一段时间,现在又成为机器学习中一种重新焕发青春的算法。
Neural Network设计初衷,是为了模拟人类大脑。 人类的大脑可以完成各种不同类别的工作,想要模拟它,似乎要写成千上万中程序去模拟不同功能,如识别物体,辨别声音,触觉感知......
但有种假说认为,大脑的方法是“实现一个学习方法”,应对所有的任务。

证据有: 切断耳朵与处理声音的大脑皮层之间的联系,把眼睛信号接上去,听觉皮层就学会了看。(视觉重连实验)
利用这一点,有人把灰度摄像头的信号通过电极传送给舌头,可以让盲人学会用舌头看。(brainPort)


1. Neural Network的必要性:
在有了Linear Regression 和 Logistics Regression之后,为何还需要Neural Network呢?
我们以classification举例如下:

Non-Linear Classification:
当原始特征(feature)本身较多时,若要使用二次或者三次多项式(高阶多项式包含到特征内),如: x1*x1, x1*x2......x1*x100. 则有NxN/2个多项式。 会使特征空间急剧膨胀。
会造成以下问题:
A. 特征数太多,容易过拟合。
B. 计算量太大。





 

机器学习Neural_Network二

$
0
0
作者:Sam (甄峰) sam_code@hotmail.com



 

Adaboost训练记录

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

1. 工具简介:
OpenCV提供了多个小程序用来训练级联检测器以及满足其它需求:
createsample: 用于产生训练程序所需的样本集文件。样本集文件(vec)在训练时会被用到。
haartraining, traincascade:训练程序。
performance: 用于测试模型的性能。(当前只能测试haartraining训练出的基于haar-like特征训练出的检测器模型)。
opencv_annotation: 用来标注图片。把正样本图片中的目标位置和大小指定出来。

haartraining, traincascade: 是由opencv提供的,用于训练物体检测模型的boost级联检测器程序。
haartraining: 是较老的版本,只支持haar-like特征。
traincascade: opencv团队目前推荐的训练程序,不仅可以支持haar-like特征,也可以使用hog和LBP特征。


2. 数据准备:
2.1:准备好正样本图片(图片中包含要检测的物体)。
Sam通常按不同的类型,把图片放到不同的目录内。
例如: 
cat目录下包含很多子目录,每个子目录内才是各个类别(只列了光线条件)的图片。
cat/top_light
cat/back_light
cat/dark



2.2:建立正样本描述文件
为了防止错误,最好使用绝对地址。

可以使用opencv_annotation来建立正样本描述文件。
opencv_annotation.exe -i=Z:\image\cat\top_light  -a=Z:\annoatation\top_light.txt
opencv_annotation.exe -i=Z:\image\cat\back_light  -a=Z:\annoatation\back_light.txt
.
.
.
opencv_annotation.exe -i=Z:\image\cat\dark  -a=Z:\annoatation\dark.txt


opencv_annotation运行程序的时候会遍历文件夹下所有的图片,每次显示一张图片,点击鼠标左键,拖动鼠标确定区域,再点一次鼠标左键,结束。按键盘的字母C为确定选择(矩形框会由红色变为绿色),字母N为下一张图片,ESC为结束此次标注。
注意,程序之后在所有图片都遍历完后才会创建指定的.txt文件,中途退出是没有的。



然后把这些txt整合成一个txt文件。作为正样本描述文件
内容格式为:

image_location number_annotations x0 y0 w0 h0 x1 y1 w1 h1 … xN yN wN hN

代表 number_annotations 个目标在image_location中标注,x0 y0 w0 h0代表左上角的点的xy坐标以及宽度和高度。


例:Z:\image\cat\top_light\0_20200325150709__10604.jpg 1 22 74 36 63



2.3:准备负样本图片:

2.4:建立负样本描述文件:
例如:文件名为:neg_image.txt
内容通常为:
G:\ImageData\neg\neg_046134.jpg


3. 建立样本集文件(.vec):
opencv_createsample.exe -vec pos.vec   -info pos_image.txt    -bg neg_image.txt        -w 18          -h 30 -num 77857

-img指定的单张图片,或者-info指定得样本集描述文件。

-vec 指定输出文件的保存位置。输出是一个以.vec结尾的文件,其中保存着生成得样本集,作为后续训练程序的输入

-num 指定样本集文件保存的样本数。


全部命令参数:

-img  源图像路径+文件名

-info  含有标定信息的样本集描述文件

-vec   含有样本集的.vec文件保存位置

-bg      背景集描述文件(负样本描述文件),用于生成混合变换得图像,将-img指定得图像放入-bg指定得背景图像,产生变换后得图像 

-num   要产生得样本数量

-bgcolor 背景色,处于bgcolor-bgthreshbgcolor+bgthresh之间的像素被视为透明的。the background color denotes the transparent color. Since there might be compression artifacts, the amount of color tolerance can be specified by –bgthresh. All pixels between bgcolor-bgthresh and bgcolor+bgthresh are regarded as transparent.

-bgthresh     背景色阈值             (灰度变化0-255)

-inv  颜色反转

-randinv    颜色随机反转

    -maxidev  样本的最大像素变化值    (灰度变化0-255)

    -maxxangle  x轴旋转得最大角度    (弧度为单位)

    -maxyangle   y轴旋转得最大角度    (弧度为单位)

    -maxzangle  z轴旋转得最大角度 (弧度为单位)

    -show  显示变换后得图片,按Esc键省略后续的显示,通常用于调试效果

-w   输出样本的宽   

-h   输出样本的高 



4. 训练:
opencv_traincascade.exe -data G:\Sam\data 
-vec G:\Sam\ThumbUpAda_Pos0601.vec 
-bg G:\Sam\ThumbUpAda_Neg_0427.txt 
-numPos 7000 -numNeg 12000 -numStages 28 -precalcValBufSize 512 -precalcIdxBufSize 512 -w 18 -h 30 -mode BASIC -baseFormatSave -maxWeakCount 300 -maxDepth 2


traincascade.exe

使用adaboost算法训练模型的程序(新版),可开启openMP加速,支持数据量较大的训练。

 

输入:

-vec,正样本集的.vec文件,由createsamples.exe程序生成

-bg,负样本描述文件

-w -h  

正样本图像的尺寸,必须跟使用createsamples创建得正样本尺寸保持一致,即.vec存储的图像尺寸

 

输出:

-data,最终模型的存放位置,模型文件以.xml结尾,中间文件亦存放于此。 这个目录必须已经存在。


全部命令参数:

-data           最终模型的存放位置(要先建好)

-vec            正样本集的.vec文件

-bg             负样本描述文件

-numPos        每级强分类器训练时用到得正样本数量

-numNeg        每级强分类器训练时用到得负样本数量

  -numStages              stages的数量(级联强分类器的层数,即强分类器的数量)

  -precalcValBufSize         缓存大小,存储预先计算得特征值,单位MB,默认200

-precalcIdxBufSize         缓存大小,存储预先计算得特征索引,单位MB,默认200

-baseFormatSave          仅在使用Haar特征时有效,如果指定,级联分类器将以老格式存储  定要加

-stageType               级联类型,BOOST

-featureType              特征类型,HAAR, LBP, HOG,默认HAAR

-w  -h           正样本图像的尺寸,必须跟使用createsamples创建得正样本尺寸保持一致,即.vec存储的图像尺寸

-bt                     训练算法类型,DAB(discrete Adaboost),RAB(RealAdaboost)

LB(LogiBoost)GAB(Gentle Adaboost)

-minHitRate              每级强分类器的最低检出率,总的最大检出率为

min_hit_rate^number_of_stages

-maxFalseAlarmRate     每级强分类器的最高误检率,总的误检率为

max_false_alarm_rate^number_of_stages,默认为0.5 

-weightTrimRate          Specifies wheter and how much weight trimming should be

used. A decent choice is 0.90.

-maxDepth              每级强分类器中弱分类器树的最大层数,If 1, then a simple

stump classifier is used, if 2 and more, then CART classifier

with number_of_splits internal (split) nodes is used

-maxWeakCount         每级强分类器中所能含有的弱分类器最大数目  目前300

-mode                  使用Haar特征时,该参数指定BASICALLUpRight等类型



训练出的模型为xml文件。


5. 使用模型预测:
CascadeClassifier object_cascade;
object_cascade.load(0525_cascade23.xml);   //载入模型
if (object_cascade.empty())
{
cout << " Load Model File Failed" << endl;
//getchar();
return -1;
}

object_cascade.detectMultiScale(GrayFrame, vecObjects, 1.1, 1, 0, Size(18, 30), Size(90, 150));




 

机器学习学习算法的评估的改进

$
0
0
作者:Sam(甄峰)  sam_code@hotmail.com

0. 问题的提出:
在构建一个机器学习系统时,如何评估系统的性能?
怎样发现系统的问题所在? 如果选择最有效的改进方向?

这一系列问题在实际工作中非常重要。

举例如下:
在预测房价的机器学习系统训练后,得到一些列参数和一个假设。 但在预测新数据时有较大误差。该如何改进?通常有以下可能的方向:
A. 增加训练样本。
B. 去掉一些特征,以减少参数个数。
C. 找到更多的特征。
D.增加一些多项式特征(如:x1*x1, x1*x2, x2*x2....)
E. 增大正则化参数 lambda.
F. 减小正则化参数 lambda.

改如何选择改进的方向?

1. 机器学习诊断法(Machine Learning diagnostic):
这个诊断法,用来判断和了解机器学习算法哪里出了问题,以及哪些改进方向才是有意义的。

1.1: 准确评估学习算法的优劣
想要改进算法或者说改进假设(hypothesis), 需要先准确的评估算法学习到的假设。

在机器学习算法训练中,希望学习到的参数在应用到代价函数 J()时,J()越小越好。 
但是否意味着 J()越小,学习算法就越好?

有一种情况:在应用训练数据计算J()和误差时,数值很小。 但应用到新的数据时,却效果很差。这就是泛化性差。就是过拟合的结果。

为了及早发现这种情况,可以把数据集(DataSet)分为两部分。---TrainingSet和TestSet. 
分别用来训练和测试。数据比例通常是70% 和30%。

首先使用TrainingSet训练出参数。 在用这个参数推理出TestSet. 看J_test()或者正确率如何。 

J_Training()是代价函数应用到TrainingSet上的数值。
J_Test()是代价很熟应道到TestSet上的数值。


1.2:偏差和方差(Bias / variance):
当一个学习算法表现不佳时,通常有几种可能性:
A. 偏差较大(欠拟合)。
B. 方差较大(过拟合)。

如果能够很快确认是高偏差(High Bias)还是高方差(High Variance)。即可快速找到有效的改进方法。

先通过实际例子,感受一下产生高偏差高方差原因




underfit(欠拟合):主要是因为假设太简单,或者神经网络中神经元太少。 导致TrainingSet的数量再多,也没法很好的拟合。导致高偏差。

overfit(过拟合):主要是假设太复杂,或神经网络中神经元数量太多(w太多)。而TrainingSet数量又太少。不足以找到正确的规律。导致高方差。


高偏差或者高方差有什么特点呢?

还是以上一个例子分析:



当维度较小时,J_Training()或者说错误率较高,因为假设太简单,无法很好的拟合数据。同样,J_Test()也很高。

当维度合适时,假设可以较好的拟合数据。泛化性也较好。J_Training()和J_Test()都不高。

当维度太大时,J_Train()很低,但泛化性不好,所以J_Test()较高。

所以总结下来:
High Bias(高偏差)时,J_Training()较高。J_Test()也很高。 主要原因是假设太简单。
High Variance(高方差)时,J_Training()比较小,J_Test()却很高。主要原因是相对数据量,假设太复杂。



1.3: 学习曲线(Learning curves)
学习曲线是种判断方法,让我们方便的测试当前学习算法训练出的假设是处于高偏差,高方差还是两者皆有。

学习曲线的思路就是:改变TrainingSet 数据的个数m. 查看m与J_Training() J_Test()之间的关系。以确定当前处于那种状态。

如果曲线呈以下情况:


分析:
A.TrainingSet数量m很小时,比如1和或者2个数据。很容易拟合。所以J_Training()很小。 可此时,肯定没有获取正确的规律,所以J_Test()一定会很大。
B. 随着m增长,J_Training()会增加。J_Test()会减小。
C. 如果随着m增加,J_Training()和J_Test()都很大,且相差不多。如图所示。说明此时欠拟合。高偏差。
这时,增加样本数量,J_Training()和J_Test()不会有多大变化。因为是假设太简单,没法学习如此复杂的信息。




如果呈现以下曲线:

分析:
A.TrainingSet数量m很小时,比如1和或者2个数据。很容易拟合。所以J_Training()很小。 可此时,肯定没有获取正确的规律,所以J_Test()一定会很大。
B. 随着m增长,J_Training()会增加。J_Test()会减小。
C. 如果随着m增加,J_Training()增大,但相对较小,J_Test()很大,远大于J_Training()。如图所示。说明此时过拟合。高方差。
这时,随着增加样本数量,J_Training()和J_Test()会响应改变。因为原始是相对于假设,数据太少,信息量不够,需要跟多数据。


2. 改进方法的选择:
既然知道了高偏差和高方差,也知道了何种原因导致。所以就可以知道:

A. 增加训练样本。 对高方差有效。
B. 去掉一些特征,以减少参数个数。 对高方差有效。
C. 找到更多的特征。 对高偏差有效(尤其是假设太简单时)
D.增加一些多项式特征(如:x1*x1, x1*x2, x2*x2....)  对高偏差有效。
E. 增大正则化参数 lambda. 对解决高方差有效。
F. 减小正则化参数 lambda. 对解决高偏差有效。

3. 神经网络与欠拟合,过拟合的关系
当神经网络的隐藏层和其中的神经元很少时(小神经网络), 参数很少,容易出现欠拟合。

当神经网络的隐藏层和神经元较多时,参数很多。训练数据太少时,容易出现过拟合。

越大型的网络,性能会越好。但计算量会加大。 


4. 图像分类在神经网络应用中的思路:
图像分类在神经网络的应用中,如何构建网络,如何优化网络的思考:
A.特征数量,通常固定。(HOG/LBP)
B. 因为神经元太多时,计算量会比较大。所以隐藏层数量,神经元数量都从小往大尝试。
C. 把DataSet分为三部分: TrainingSet1(占20%)。 TrainingSet2(占50%), TestSet(占30%)
D. OpenCV ANN_MLP没有正则项参数。所以不考虑。

训练过程:
1. 从小神经网络开始,使用TrainingSet1开始训练。(训练轮次不要太高。以节约训练时间). 若出现欠拟合。则增加神经元和隐藏层。
2. 逐渐复杂网络,直到出现过拟合。则使用TrainingSet1+TrainingSet2训练。
后期再调整网络复杂度。直到平衡。










 

神经网络与深度学习初起步

$
0
0
作者: Sam(甄峰)  sam_code@hotmail.com

之前学习了机器学习基础知识,是为了以此为开始,学习神经网络与深度学习。
现在开始神经网络与深度学习的学习。

1. 神经网络与机器学习的关系:
机器学习(Machine Learning)的定义:
Arthur Samuel(1959)对Machine Learning的定义: 在无明确设置的情况下,使计算机具有学习能力的研究领域。 (Arthur Samuel 编写了一个跳棋算法,让他们自己对弈,实现了棋力的提升)
Tom Michell(1998)对Machine Learning 的定义:计算机程序从经验E中学习,解决某一个任务T, 进行某一个性能度量P。 通过P测定在T上的表现因经验E而提升。

机器学习算法包含很多,如线性回归,逻辑回归,SVM.  神经网络是机器学习的其中一种算法。


2. 神经网络在实际生产中的应用方向:
A. 一些分类和回归任务, 常用标准神经网络。或者人工神经网络。
B.  图像领域,经常用卷积神经网络--CNN.
C. 类似语言识别这样带有时间信息的序列数据,则使用循环神经网络--RNN.





3. 结构化数据和非结构化数据:
3.1: 结构化数据(Structured data):
数据有一定格式,特征数确定,每个特征都有清晰定义。
类似:房屋价格预测,肿瘤类型预测等。

3.2:非结构化数据(Unstructured data):
类似音频,图片。

非结构化数据比结构化数据更难让计算机理解。而人类则非常善于处理非结构化数据,比如人类可以很容易的理解图像信息,理解语言,序列文字等。

神经网络,深度学习让计算机更好的理解非结构化数据。


4. 深度学习兴起的原因:
MP神经元的提出是上个世纪中期的事情了,神经网络也提出很多年了。但为何现在突然兴起。除了算法上有突破外(LeNet, AlexNet等卷积思想提出)。 还有几个深层次原因。


如图所示,在数据数量较少的阶段,传统机器学习算法与神经网络的性能很难说谁更好。

但随着数据极大的增加,大规模的神经网络性能会持续提升。

随着时代的发展,当前收集海量数据成为可能。

而且,要使用海量数据进行训练,需要强大的计算能力。而当前CPU,GPU,NPU的发展,计算能力得到极大的提升。

所以这几个因素叠加,让大型神经网络和深度学习可以发挥自己的实力而脱颖而出。





 

Python使用记录

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

屡次使用python2.x python3. 记录备忘。

1. Python的函数。

1.1:

1.2:匿_名函数:
1.2.1: lambda简介:
python使用 lambda来创建匿_名函数。
lambda的主体是一个表达式,而不是一个代码块,仅仅能在lambda表达式中封装有限的逻辑。
它有着自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。

lambda使用方法如下:
lambda [arg1 [,arg2,.....argn]]: Exp
例如:
sum = lambda x, y: x+y

s1 = sum(2,3)
print("Sum is:", s1)

结果为:
('Sum is:', 5)

lambda与C/C++中内联函数的区别:
C/C++中的内联函数是为了调用小函数时不占用栈内存从而增加运行效率。 而lambda并不提高效率,只是为了使得代码简练清晰。


1.2.2.:与filter, map, reduce组合使用:
Python提供了几个函数方便用户使用---filter, map, reduce。他们支持把函数作为参数。

filter:
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。

该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。



所以可以使用lambda创建一个匿_名函数,作为filter的第一个参数。
参数
function -- 判断函数。
iterable -- 可迭代对象。
返回值
返回一个迭代器对象(Python3)
Python2中,则返回列表。

Python2:
old_v = [1,2,3,4,5,6,7,8]

new_v = filter(lambda x:x % 2 == 0, old_v)
print(new_v)
结果:
[2, 4, 6, 8]

map():
描述
map() 会根据提供的函数对指定序列做映射。

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

语法
map() 函数语法:

map(function, iterable, ...)
参数
function -- 函数
iterable -- 一个或多个序列

返回值
Python 2.x 返回列表。
Python 3.x 返回迭代器。

例:
old_v = [1,2,3,4,5,6,7,8]
plus_v = [2,3,4,5,6,7,8,9]


plus_new = map(lambda x, y: x+y, old_v, plus_v)
print(plus_new)

结果:
[3, 5, 7, 9, 11, 13, 15, 17]


reduce()
描述
reduce() 函数会对参数序列中元素进行累积。

函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

语法
reduce() 函数语法:

reduce(function, iterable[, initializer])
参数
function -- 函数,有两个参数
iterable -- 可迭代对象
initializer -- 可选,初始参数
返回值
返回函数计算结果。



2. 迭代器和生成器:
2.1:迭代器(iterator):
迭代是Python最强大的功能之一。是访问集合元素的一种方式。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完而结束。 迭代器只能前进不能后退。
迭代器有两个基本方法: iter(), next()
iter()用来创建迭代器对象。它返回一个特殊的迭代器对象,这个对象实现了next()方法。
next()逐一返回迭代器对象中的元素。当没有数据可用时,会引发:StopIteration异常。

利用迭代器对象,可是使用for....in方式逐个访问集合内元素。


字符串,元祖(tuple)或者列表(List)对象都可以用于创建迭代器。文件对象(相当于文件描述符)也可以用来创建迭代器--文件迭代器。(见注1:可迭代对象),

例1:List, tuple, 字符串对象用来创建迭代器。

#List
list1 = [1,2,3,4,5,"sam", "test"]
it = iter(list1)
print("Iter1",next(it))
for i in it:
    print(i)


Iter1 1
2
3
4
5
sam
test


注意:it是一个迭代器,可以通过next(it)和for 去取下一个元素。 但它只能从前往后取。 所以第一个next(it)取出一个元素后。 for则从下一个开始取。


#Tuple
tuple1 = ("t1", "t2", 9,8,7)
it = iter(tuple1)
print("Iter2")
for i in it:
    print(i)


Iter2
t1
t2
9
8
7
   
#String
var1 = "Hello World!"
it = iter(var1)
print("Iter3:")
for i in it:
    print(i)



Iter3:
H
e
l
l
o
 
W
o
r
l
d
!

#文件迭代器
逐行显示文件内容。
for i in open("test.txt", encoding="utf-8"):
    print(i)

也可以自定义迭代器。
例:



2.2: 生成器(generator):
生成器是个函数,与普通函数不同的是,它返回一个迭代器。
生成器的表示是使用yield.
在调用生成器过程中,每次遇到yield时,函数会暂停并保存当前所有运行信息,返回yield的值,并在下一次调用时从当前位置继续执行。

def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
        
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
for i in f:
    print(i)


0
1
1
2
3
5
8
13
21
34
55


3. 文件夹的遍历:
3.1. os.walk()

 os.walk(top, topdown=True, onerror=None, followlinks=False)
os.walk()用来遍历目录中的子目录和文件。它本身是个生成器(generator)---Directory tree generator
所以它的返回值就是一个迭代器。---yields a 3-tuple
分别为:dirpath, dirnames, filenames
代表了:
dirpath: 是个string.当前正在遍历的目录。
dirnames:是个list. 内容是dirpath目录内所有目录名(不包括子目录,不包括 . 和 ..)
filenames:是个list.内容是dirpath目录内所有文件名。(不包括子目录内的文件)

参数: 
top: 要遍历的目标目录。
topdown: True,则优先遍历top目录。  false:优先遍历top的子目录。

例:
for root,dirs,files in os.walk("../"):
    print("\nroot:", root)
    print("Dir:")
    for dir in dirs:
        print(dir)
    print("Files:")    
    for file in files:
        print(file)
因为是个迭代器, 所以可以用 for.....in ....



3.2: os.listdir()

os.listdir(path=None)
返回一个list. 包含目录path内左右文件名(包括目录名)。
for file in os.listdir("../"):
    print(file)





注1:
可迭代对象(iterable):
可迭代对象是能够逐一返回其成员项的对象。包括序列类型(如:list, 字符串,tuple)和非序列类型(如dict,文件对对象,定义__iter__()方法的任何自定义类对象)。
一个可迭代对象都有一个__iter__()方法。 也就是说,有 __iter__()方法的对象,就是可迭代对象。

注2:
迭代器,生成器相关:

问题一:既然可迭代对象也可以使用for循环遍历,为何还要使用迭代器呢?

一般情况下不需要将可迭代对象封装为迭代器。但是想象一种需要重复迭代的场景,在一个class中我们需要对输入数组进行正序、反序、正序step=1、正序step=2等等等等的多种重复遍历,那么我们完全可以针对每一种遍历方式写一个迭代容器,这样就不用每次需要遍历时都费劲心思的写一堆对应的for循环代码,只要调用相应名称的迭代器就能做到,针对每一种迭代器我们还可以加上类型判断及相应的处理,这使得我们可以不必关注底层的迭代代码实现。

从这种角度来看,你可以将迭代器看做可迭代对象的函数化,有一个非常流行的迭代器库itertools,其实就是如上所说的,他为很多可迭代类型提前定义好了一些列的常见迭代方式,并封装为了迭代器,这样大家就可以很方便的直接通过调用此模块玩转迭代。

此外iterator还可以节省内存,这点在问题二会描述。

问题二:生成器(generator)如何节约内存的?

generator的标志性关键字yield其实可以看作return,以本文上述的generator_list()方法为例,generator_list(a)就是一个生成器。

生成器最大的好处在于:generator_list(a)并不会真正执行函数的代码,只有在被循环时才会去获取值,且每次循环都return一个值(即yield一个值),在处理完毕后下次循环时依然使用相同的内存(假设处理单位大小一样)来获取值并处理,这样在一次for循环中函数好像中断(yield)了无数次,每次都用相同大小的内存来存储被迭代的值。

yield与return的最大区别就是yield并不意味着函数的终止,而是意味着函数的一次中断,在未被迭代完毕之前yield意味着先返回一次迭代值并继续下一次函数的执行(起始位置是上一次yeild语句结束),而return则基本意味着一个函数的彻底终止并返回一个全量的返回值。

因此generator是为了节省内存的,而且将函数写为一个生成器可以使函数变的可迭代,如果我们想遍历函数的返回值,我们不用再单独定义一个可迭代变量存储函数的返回值们,而是直接迭代生成器函数即可(除非函数本身返回一个全量的可迭代对象)。

同理iterator的__iter__()方法只是一个迭代的入口,每次调用__next__()时返回一个迭代值,同样以O(1)的空间复杂度完成了迭代。

问题三:iterator与generator的异同?

generator是iterator的一个子集,iterator也有节约内存的功效,generator也可以定制不同的迭代方式。






 

Anaconda和Jupyter使用

$
0
0
作者: Sam(甄峰) sam_code@hotmail.com

1. ipynb文件的import:
在Jupyter Notebook中建立Python文件时,文件后缀名为ipynb. 那如何import另一个ipynb文件呢?
如创建了Test_Import.ipynb, 如何在另一个ipynb文件中import呢?
A. 首先安装:import_ipynb
pip install import_ipynb


import import_ipynb
from Test_Import import x_data1

print(x_data1)




 

神经网络和深度学习卷积神经网络初探

$
0
0
作者: Sam (甄峰)  sam_code@hotmail.com

神经网络和深度学习在推动计算机视觉方面成效显著。其中卷积神经网络应用最广泛。

1. 计算机视觉的应用方向和遇到的挑战:
1.1:计算机视觉主要有以下几种应用方向:
A. Image classification. 
图像分类,分辨图片是否是某种物体。
B. Object Detection:
目标检测,不但要识别出图片中的物体,还要指出它们的位置。
C. Neural Style Transfer:
图片风格迁徙。



1.2:计算机视觉遇到的挑战
因为计算机视觉研究对象是图片,而大图片则数据量极大,例如:1024x1024的RGB图像,为1024x1024x3=3145728个byte.
如果采用普通的神经网络全连接,哪怕第一层仅仅有1000个神经元。则第一层的参数w[1]就有3145728x1000个。如此巨大的参数量,为了防止过拟合,需要极多的数据进行训练。数据量太大,计算量太大,所需计算能力和内存过于庞大。

卷积神经网络,可以解决这个难题。

卷积操作是卷积神经网络最基础的操作,可以通过边缘检测为例,看看卷积操作具体是如何运作的。

2. 卷积操作相关
2.1. 卷积操作--以边缘检测为例:
如何检测图片中物体的垂直/水平边缘。
可以使用一个卷积核/filter. 对目标图片进行卷积操作。


可以看到,输入Image左边亮,右边暗。经过卷积操作,输出一个数据。中间有明显一道垂直数据。表示有一个边缘。


边缘的正边和负边:
如何判断边缘从左到右方向,或由上到下方向上,是由亮变暗,还是由暗变量呢,或者说,是正边还是负边呢?

可以看到,因为此卷积核左亮右暗,所以目标图片如果由亮变暗,则边缘数据呈正值(正边)。 否则,就是负值(负边)。


水平和垂直边界,则采用不同的卷积核/filter:


不同的卷积核,可以获取不同的特征。
对边缘检测,卷积核/filter还有其它选择,如:Sobel filter, Schaar filter.

这些都是手工写的卷积核/filter,不同的卷积核/filter可以获取不同的信息。但如果把卷积核/filter内的每个值都做为参数w,用反向传播法计算,可以学习到非常好的边缘检测器,它们的数据捕捉能力可能远超这些手写的卷积核/filter. 如,检测45度,70度,87度或任意角度的边缘。甚至是其它的特征。




将卷积核/filter中的数字视为参数并训练的思想,成为计算机视觉中最有效的思路之一


2.2. Padding:
Padding是卷积操作中一个操作概念。
3.1:缘由:
为何6x6的一个Image,经过3x3的一个filter卷积后,产生一个4x4的结果。
因为6x6的Image中放入3x3的fiter,只有4x4种可能的位置。
所以, nxn的image,经过fxf的filter的卷积。会产生 (n-f+1)x(n-f+1)的结果。

有什么缺点呢?
A. 每次卷积后,Image会变小。当层数太多时,Image会太小。
B. 边,角地带的数据,在输出中参与较少。以为着丢掉了边角地带的信息。

可能的解决方案:
在原始的图片之外,加上一圈或几圈像素,填充0.
例如:边缘处加上P个像素。
则(n+2p-f+1)x(n+2p-f+1).

Padding的方式有两种:
Valid padding: 不填充。 则产生 (n-f+1)x(n-f+1)的结果。
Same Padding:每个边缘填充p像素。则产生(n+2p-f+1)x(n+2p-f+1)的结果。
如果步长不为一,则看Strided部分。

2.3. 卷积步长(Strided)
这是卷积操作中另一个概念,步长。 指卷积核/filter在Image上移动的步长。



2.4. 多维Image的卷积:
如果不想只获取灰度图的特征,而是想获取RGB图像的特诊,就要对HxWx3的三维数据进行卷积。


如图所示,6x6x3的Image,被3x3x3的卷积核卷积。产生4x4x1的的结果。
此处需要注意两点:
A. 卷积核/filter的第三个维度数(3)需要和Input Image的第三个维度数(3)完全相同。如此,才能正常计算。
B. 输出特征的第三个维度是1.

2.5. Multiple filter:
多个filter.



一个卷积核/filter代表一种特征,则如果想要同时获取多个特征,则可以使用多个filter.
有多少个卷积核/filter. 则生成的特征就有几个。 也就是第三个维度数。


3. 卷积神经网络相关:
3.1: Pooling(池化):
在一次卷积操作后,可以对数据进行Pooling操作。这个操作的手段是减小数据量。目的是缩减模型大小,提高计算速度。

常用的Pooling:
A.最大池化。
B.平均池化。

最大池化是在池化框内,挑选出最大的值作为输出。最大池化通常不进行padding.
平均池化则是在池化框内,计算出平均值作为输出。

关于池化,有两点需要注意:
A. 维度:

B.池化操作不影响Channel数。
如24x24x3的Image做pooling. 步长s=2. f=2. 则输出12x12x3. channel还是为3.


3.2:卷积神经网络典型的一层:
如有64x64x3的Image。使用10个3x3x3的filter.  Valid padding
则卷积操作输出 62x62x10的数据。每个filter对应一个bias. 
每个数据加对应bias之后, 使用激活函数(如sigmod,relu)增加非线性.

则共有(3x3x3+1)x10=280个参数。 不管图片是64x64.还是1024x1024, 参数都是这么多。

后续再使用pooling. f=2, s=2.
则输出为31x31x10.  (pooling不改变channel数)

3.3: 卷积神经网络一般的构成:

即通常是:
conv1-pooling--conv2-pooling-....-FC1--FC2--softmax


3.3:卷积神经网络的优势:
与全连接神经网络相比,卷积神经网络有以下优势:
1. 参数共享:
以为如果一个特征提取器(filter)适合于Image的一本部分,那它也同样适合于另一个部分。所以这个filter参数对整个Image是共享的。
如果是全连接,1024x1024x3的Image,和1000个神经元全连接,W【1】太大。而使用filter,则大幅降低参数个数。
2. 稀疏连接:
卷积中,某个后一层的数据,只与前一层fxf个数据相关,与其它数据不相关。(局部感知野)

所以,卷积神经网络,通过更小的参数,获取了足够的特征信息。
















 
Viewing all 158 articles
Browse latest View live