当前位置: 首页 > 编程日记 > 正文

一站式解决:隐马尔可夫模型(HMM)全过程推导及实现

640?wx_fmt=png

作者 | 永远在你身后
转载自知乎用户永远在你身后

【导读】隐马尔可夫模型(Hidden Markov Model,HMM)是关于时许的概率模型,是一个生成模型,描述由一个隐藏的马尔科夫链随机生成不可观测的状态序列,每个状态生成一个观测,而由此产生一个观测序列。

定义抄完了,下面我们从一个简单的生成过程入手,顺便引出HMM的参数。

假设有4个盒子,每个盒子里面有不同数量的红、白两种颜色的球,具体如下表:

640?wx_fmt=jpeg

本栗子引用自《统计学习方法》

现在从这些盒子中抽取若干( 640?wx_fmt=svg )个球,每次抽取后记录颜色,再放回原盒子,采样的规则如下:

开始时,按照一个初始概率分布随机选择第一个盒子,这里将第一个盒子用 640?wx_fmt=svg 表示:

640?wx_fmt=png

640?wx_fmt=png的值用变量640?wx_fmt=png 表示。因为有4个盒子可共选择,所以 640?wx_fmt=png。然后随机从该盒子中抽取一个球,使用640?wx_fmt=png表示:

640?wx_fmt=png

640?wx_fmt=png 的值用变量640?wx_fmt=png 表示。因为只有两种球可供选择,所以640?wx_fmt=png 。一共有4个箱子,2种球,结合前面的箱子的详细数据,可以得到从每一个箱子取到各种颜色球的可能性,用一个表格表示:

640?wx_fmt=jpeg

进一步,可以用一个矩阵(称为观测概率矩阵,也有资料叫做发射矩阵)来表示该表

640?wx_fmt=svg

其中 640?wx_fmt=svg 表示在当前时刻给定 的条件下,给定640?wx_fmt=png

640?wx_fmt=svg 表示当前的时刻,例如现在是第1时刻;然后是前面标注的初始概率分布,这个概率分布可以用一个向量(称作初始状态概率向量)来表示:

640?wx_fmt=png

其中的640?wx_fmt=png

640?wx_fmt=png

例如该分布是均匀分布的话,对应的向量就是

640?wx_fmt=png

记录抽取的球的颜色后将其放回,然后在按照如下规则选择下一个盒子(640?wx_fmt=png):

  • 如果当前是盒子1,则选择盒子2
  • 如果当前是盒子2或3,则分布以概率0.4和0.6选择前一个或后一个盒子
  • 如果当前是盒子4,则各以0.5的概率停留在盒子4或者选择盒子3

640?wx_fmt=jpeg

同样,也可以根据以上规则做出一个表格,其中首列表示当前盒子,首行表示下一个盒子

640?wx_fmt=jpeg

同样使用一个矩阵(称为状态转移矩阵)来表示上表

640?wx_fmt=png


640?wx_fmt=png

以上,生成过程的主要流程就介绍完了,简单概括就是:盒子,取球,盒子,取球……直到生成指定数量(T)的数据后停止。如果对这个过程还有不太理解的话,可以看看文章开头给出的关于马尔科夫链的链接。

现在,整理一下参数:有两个矩阵,一个向量:

640?wx_fmt=png


其中N表示隐变量z的状态数量,M表示观测变量x可能的取值数量,在后面的讨论中,用640?wx_fmt=png表示所有的参数。下面,根据这个栗子,写一个数据生成代码

import numpy as np	class HMM(object):	def __init__(self, N, M, pi=None, A=None, B=None):	self.N = N	self.M = M	self.pi = pi	self.A = A	self.B = B	def get_data_with_distribute(self, dist): # 根据给定的概率分布随机返回数据(索引)	r = np.random.rand()	for i, p in enumerate(dist):	if r < p: return i	r -= p	def generate(self, T: int):	'''	根据给定的参数生成观测序列	T: 指定要生成数据的数量	'''	z = self.get_data_with_distribute(self.pi)    # 根据初始概率分布生成第一个状态	x = self.get_data_with_distribute(self.B[z])  # 生成第一个观测数据	result = [x]	for _ in range(T-1):        # 依次生成余下的状态和观测数据	z = self.get_data_with_distribute(self.A[z])	x = self.get_data_with_distribute(self.B[z])	result.append(x)	return result	if __name__ == "__main__":	pi = np.array([.25, .25, .25, .25])	A = np.array([	[0,  1,  0, 0],	[.4, 0, .6, 0],	[0, .4, 0, .6],	[0, 0, .5, .5]])	B = np.array([	[.5, .5],	[.3, .7],	[.6, .4],	[.8, .2]])	hmm = HMM(4, 2, pi, A, B)	print(hmm.generate(10))  # 生成10个数据	# 生成结果如下	
[0, 0, 1, 1, 1, 1, 0, 1, 0, 0]   # 0代表红球,1代表白球

现在,参数介绍完了,数据生成过程也了解了,接下来就是解决HMM的基本问题了,一共有三个

640?wx_fmt=png


不过,在讨论这三个问题的相关算法之前,首先要给出两个假设,在后面的推导过程中会不断的用到:
  1. 齐次马尔可夫假设:即任意时刻的状态只依赖于前一时刻的状态,与其他时刻的状态无关(当然,初始时刻的状态由参数π决定):

    640?wx_fmt=png

  2. 观测独立假设:即任意时刻的观测只依赖于该时刻的状态,与其他无关:概率计算算法

    640?wx_fmt=png


概率计算算法

现在,来看第一个问题,关于概率的计算,由于存在隐变量,所以X的边际概率需要将所有的联合概率 640?wx_fmt=svg加和得到:

640?wx_fmt=png

由于给出了T个观测数据,所以相应的状态也有T 个:

640?wx_fmt=png

将(1)式中的640?wx_fmt=png展开得到:

640?wx_fmt=png

即使不考虑内部的计算,这起码也是640?wx_fmt=png 阶的计算量,所以需要更有效的算法,下面介绍两种:前向算法和后向算法

设有T 个序列,如下图所示:

640?wx_fmt=jpeg

现在定义一个前向概率640?wx_fmt=png,它t时刻的状态以及1,2,...t时刻的观测在给定参数下的联合概率:

640?wx_fmt=png

也就是下图中标记的那一部分

640?wx_fmt=jpeg

根据定义,可以得到它的初值:

640?wx_fmt=png

其中 640?wx_fmt=svg表示由状态640?wx_fmt=png生成给定观测数据的概率,例如设t时刻观测数据 640?wx_fmt=svg,有

640?wx_fmt=png

接着,根据(2)式,还可以得到:

640?wx_fmt=png

由此公式,遍历640?wx_fmt=png的取值求和,可以得到 640?wx_fmt=svg 的边际概率

640?wx_fmt=png

640?wx_fmt=png


首先,来看上式中红色部分,根据观测独立假设(下文再引用该假设时称作假设2):

640?wx_fmt=png

然后是蓝色部分,根据齐次马尔可夫假设(下文再引用该假设时称作假设1)

640?wx_fmt=png


将上述结果代入(3)式,得到

640?wx_fmt=png


以上就是前向算法的推导,下面根据一个栗子来写代码,假设前面抽了五个球,分别是:红、红、白、白、红,求概率
class HMM(object):	def evaluate(self, X):	'''	根据给定的参数计算条件概率	X: 观测数据	'''	alpha = self.pi * self.B[:,X[0]]	for x in X[1:]:	# alpha_next = np.empty(self.N)	# for j in range(self.N):	#     alpha_next[j] = np.sum(self.A[:,j] * alpha * self.B[j,x])	# alpha = alpha_next	alpha = np.sum(self.A * alpha.reshape(-1,1) * self.B[:,x].reshape(1,-1), axis=0)	return alpha.sum()	print(hmm.evaluate([0,0,1,1,0]))   # 0.026862016
上面的注释中的代码是按照公式来写的,可以看出,时间复杂度降为了640?wx_fmt=png,比之前至少640?wx_fmt=png的起步价已经好太多了.

接着,再讨论后向算法,首先定义后向概率:

640?wx_fmt=png


也就是下图中的部分


640?wx_fmt=jpeg


并且规定初始值

640?wx_fmt=png


根据(4)式,还可以得到

640?wx_fmt=png

640?wx_fmt=png


另外说一点,如果对于前向算法还有印象的话,你会发现:上面640?wx_fmt=png的定义。实际上,对于任意时刻t,存在以下等式

640?wx_fmt=svg

640?wx_fmt=png

观察上式,蓝色部分自然就是640?wx_fmt=png。而红色部分,根据假设2,640?wx_fmt=png都是无关(即互相独立),可以省去,所以这部分最终变为:

640?wx_fmt=png

640?wx_fmt=png

推导完毕,上代码
def evaluate_backward(self, X):	beta = np.ones(self.N)	for x in X[:0:-1]:	beta_next = np.empty(self.N)	for i in range(self.N):	beta_next[i] = np.sum(self.A[i,:] * self.B[:,x] * beta)	beta = beta_next	return np.sum(beta * self.pi * self.B[:,X[0]])
和前向算法差不多,而且是照着公式写的,就不写注释了,还是使用前面的栗子,跑了一下发现结果是一样的。我想,同时写两个BUG得出同一个结果的概率应该很小很小吧


学习算法


现在,概率计算的问题就解决了,接着来看第二个问题,参数学习,这里需要用到EM算法,不熟悉的可以参考一下:https://zhuanlan.zhihu.com/p/85236423

640?wx_fmt=png


然后,对Q函数中的每一项进行化简,首先是第一项,用到了齐次马尔可夫假设:

640?wx_fmt=png

640?wx_fmt=png

接着是第二项,用到了观测独立假设

640?wx_fmt=png

又因我们要求使Q函数最大化的参数,即:

640?wx_fmt=png


640?wx_fmt=png

将结果代入(5)式,得到

640?wx_fmt=png


其中, 640?wx_fmt=png就是当前参数下观测数据的概率,就是第一个问题所求解的。另外,利用第一个问题中定义的前向概率和后向概率,有:

640?wx_fmt=png

最终得到:

640?wx_fmt=svg

接着来看矩阵A的迭代公式

640?wx_fmt=png


同样,将上式化简,另外为了在后面方便引用,将该式设为一个函数f

640?wx_fmt=png


可以看到,一共是 640?wx_fmt=svg个相似的项,我们提一个(红色部分)出来化简,看看能不能找到通项公式

640?wx_fmt=png


这样,就化简出了通向公式,将它代入f中,得到

640?wx_fmt=png


因为 640?wx_fmt=svg是一个概率分布的矩阵,例如前面的栗子,每一行的和等于1

640?wx_fmt=jpeg

所以A是有约束的:
640?wx_fmt=svg

同样,使用拉格朗日乘数法,构造目标函数

640?wx_fmt=svg

将该函数对矩阵A的每一个元素求(偏)导并令导数为0:

640?wx_fmt=png

将两边同时乘上 640?wx_fmt=svg,得到640?wx_fmt=png

640?wx_fmt=png

注意一下上面的下标t与上标中(t+1)它们是不同的,由于变量比较多,各种ijk比较多,所以这里需要注意一下。然后利用640?wx_fmt=png的约束,代入(6)式,得到:

640?wx_fmt=png

然后化简:

640?wx_fmt=png

代入(7)式,得到

640?wx_fmt=png

(8)式中,分母部分前面已经解决了,下面来看分子部分,进行化简

640?wx_fmt=png


注意,上面的化简中, 640?wx_fmt=svg。然后红色和蓝色部分的化简用到了前面前面提过的两个假设,将条件中不被依赖的变量去掉了。最后代入(8)式得到:

640?wx_fmt=png

最后,就剩观测概率矩阵(B)的迭代公式

640?wx_fmt=png


同样,拆开化简

640?wx_fmt=png


分析第一项:

640?wx_fmt=png

代入f,得到

640?wx_fmt=png


以前面的栗子为例,矩阵B同样有约束

640?wx_fmt=jpeg

也是要求每一行的和等于1

640?wx_fmt=png

M是矩阵B的列数,前面已经定义过的,构造拉格朗日函数:

640?wx_fmt=png

将该函数对矩阵B的每一项元素求导,得到:

640?wx_fmt=png

640?wx_fmt=png

同样利用B的约束条件,得到

640?wx_fmt=png


化简得到(9)式的分母640?wx_fmt=png

根据上面的公式,直接敲代码了
class HMM(object):	def fit(self, X):	'''	根据给定观测序列反推参数	'''	# 初始化参数 pi, A, B	self.pi = np.random.sample(self.N)	self.A = np.ones((self.N,self.N)) / self.N	self.B = np.ones((self.N,self.M)) / self.M	self.pi = self.pi / self.pi.sum()	T = len(X)	for _ in range(50):	# 按公式计算下一时刻的参数	alpha, beta = self.get_something(X)	gamma = alpha * beta	for i in range(self.N):	for j in range(self.N):	self.A[i,j] = np.sum(alpha[:-1,i]*beta[1:,j]*self.A[i,j]*self.B[j,X[1:]]) / gamma[:-1,i].sum()	for j in range(self.N):	for k in range(self.M):	self.B[j,k] = np.sum(gamma[:,j]*(X == k)) / gamma[:,j].sum()	self.pi = gamma[0] / gamma[-1].sum()	def get_something(self, X):	'''	根据给定数据与参数,计算所有时刻的前向概率和后向概率	'''	T = len(X)	alpha = np.zeros((T,self.N))	alpha[0,:] = self.pi * self.B[:,X[0]]	for i in range(T-1):	x = X[i+1]	alpha[i+1,:] = np.sum(self.A * alpha[i].reshape(-1,1) * self.B[:,x].reshape(1,-1), axis=0)	beta = np.ones((T,self.N))	for j in range(T-1,0,-1):	for i in range(self.N):	beta[j-1,i] = np.sum(self.A[i,:] * self.B[:,X[j]] * beta[j])	return alpha, beta	if __name__ == "__main__":	import matplotlib.pyplot as plt	def triangle_data(T):   # 生成三角波形状的序列	data = []	for x in range(T):	x = x % 6	data.append(x if x <= 3 else 6-x)	return data	data = np.array(triangle_data(30))	hmm = HMM(10, 4)	hmm.fit(data)               # 先根据给定数据反推参数	gen_obs = hmm.generate(30)  # 再根据学习的参数生成数据	x = np.arange(30)	plt.scatter(x, gen_obs, marker='*', color='r')	plt.plot(x, data, color='g')	plt.show()
上面的代码,使用最开始的栗子无法收敛,或者收敛到坑里(公式和书上《统计学习方法》是一样的),但是使用别人的例子又能很好的工作。调了一晚上后我觉得还是把它贴上来算了,希望大神发现了问题所在能告知一下。


预测算法


最后一个问题了,解决这个问题的算法叫做维特比(Viterbi)算法。实际上它是一个动态规划求解最优路径的算法,这里的最优路径不过就是对应成最大概率而已,比前面两个问题容易解决得多。直接上例子,如下图所示:

640?wx_fmt=jpeg

640?wx_fmt=png


640?wx_fmt=jpeg


640?wx_fmt=png

如果 640?wx_fmt=svg,那么最优路径(索引)自然就是

640?wx_fmt=png

接着,假设还有 640?wx_fmt=png,末端的计算自然还是一样

640?wx_fmt=jpeg

问题在于从 640?wx_fmt=png如何计算最大概率

640?wx_fmt=jpeg

640?wx_fmt=png

然后,又因为我们所求的是路径,所以还要记录最大概率所对应的索引值

640?wx_fmt=png

举个具体的例子:

640?wx_fmt=png

640?wx_fmt=png

最后,就是回溯最优路径,因为已知

640?wx_fmt=png

640?wx_fmt=png

不多说了,上代码:
class HMM(object):	def decode(self, X):	T = len(X)	x = X[0]	delta = self.pi * self.B[:,x]	varphi = np.zeros((T, self.N), dtype=int)	path = [0] * T	for i in range(1, T):	delta = delta.reshape(-1,1)     # 转成一列方便广播	tmp = delta * self.A	varphi[i,:] = np.argmax(tmp, axis=0)	delta = np.max(tmp, axis=0) * self.B[:,X[i]]	path[-1] = np.argmax(delta)	# 回溯最优路径	for i in range(T-1,0,-1):	path[i-1] = varphi[i,path[i]]	return path

(*本文为 AI科技大本营转载文章,载请联系作者


精彩推荐



2019 中国大数据技术大会(BDTC)再度来袭!豪华主席阵容及百位技术专家齐聚,15 场精选专题技术和行业论坛,超强干货+技术剖析+行业实践立体解读,深入解析热门技术在行业中的实践落地。

即日起,限量 5 折票开售,数量有限,扫码购买,先到先得!
640?wx_fmt=png

推荐阅读

  • 确认!语音识别大牛Daniel Povey将入职小米,曾遭霍普金斯大学解雇,怒拒Facebook

  • 大规模1.4亿中文知识图谱数据,我把它开源了

  • 自动驾驶关键环节:行人的行为意图建模和预测(上)

  • 不足 20 行 Python 代码,高效实现 k-means 均值聚类算法

  • 巨头垂涎却不能染指,loT 数据库风口已至

  • 【建议收藏】数据中心服务器基础知识大全

  • 从4个维度深度剖析闪电网络现状,在CKB上实现闪电网络的理由 | 博文精选

  • 身边程序员同事竟说自己敲代码速度快!Ctrl+C、Ctrl+V ?

  • 100 美元一行代码,开源软件到底咋赚钱?

640?wx_fmt=png

你点的每个“在看”,我都认真当成了AI

相关文章:

CUDA Samples: Image Process: BGR to BGR565

图像像素格式BGR565是每一个像素占2个字节&#xff0c;其中Blue占5位&#xff0c;Green占6位&#xff0c;Red占5位。在OpenCV中&#xff0c;BGR到BGR565的每一个像素的计算公式是&#xff1a;unsigned short dst (unsigned short)((B >> 3) | ((G & ~3) << 3)…

NoSQL数据库探讨 - 为什么要用非关系数据库?

源地址&#xff1a;http://robbin.javaeye.com/blog/524977 随着互联网web2.0网站的兴起&#xff0c;非关系型的数据库现在成了一个极其热门的新领域&#xff0c;非关系数据库产品的发展非常迅速。而传统的关系数据库在应付web2.0网站&#xff0c;特别是超大规模和高并发的SNS类…

手机内存RAM、ROM简介

手机内存包含两个&#xff1a;一个是运行内存(RAM)&#xff0c;一个是机身内存(ROM)。两者的功能有所不同&#xff0c;运行内存是对手机操作系统和其它程序运行过程中&#xff0c;产生的临时数据进行存储的媒介。如果手机运行的程序比较多&#xff0c;占用运行内存空间较大&…

一个月入门Python爬虫,轻松爬取大规模数据

如果你仔细观察&#xff0c;就不难发现&#xff0c;懂爬虫、学习爬虫的人越来越多&#xff0c;一方面&#xff0c;互联网可以获取的数据越来越多&#xff0c;另一方面&#xff0c;像 Python这样一个月入门Python爬虫&#xff0c;轻松爬的编程语言提供越来越多的优秀工具&#x…

软件包管理 之 软件在线升级更新yum 图形工具介绍

作者&#xff1a;北南南北来自&#xff1a;LinuxSir.Org提要&#xff1a;yum 是Fedora/Redhat 软件包管理工具&#xff0c;包括文本命令行模式和图形模式&#xff1b;图形模式的yum也是基于文本模式的&#xff1b;目前yum图形前端程序主要有 yumex和kyum &#xff1b; 正文一、…

[PHPUnit]自动生成PHPUnit测试骨架脚本-提供您的开发效率【2015升级版】

2019独角兽企业重金招聘Python工程师标准>>> 场景 在编写PHPUnit单元测试代码时&#xff0c;其实很多都是对各个类的各个外部调用的函数进行测试验证&#xff0c;检测代码覆盖率&#xff0c;验证预期效果。为避免增加开发量&#xff0c;可以使用PHPUnit提供的phpuni…

ORL Faces Database介绍

ORL人脸数据集共包含40个不同人的400张图像&#xff0c;是在1992年4月至1994年4月期间由英国剑桥的Olivetti研究实验室创建。此数据集下包含40个目录&#xff0c;每个目录下有10张图像&#xff0c;每个目录表示一个不同的人。所有的图像是以PGM格式存储&#xff0c;灰度图&…

张俊林:BERT和Transformer到底学到了什么 | AI ProCon 2019

演讲嘉宾 | 张俊林&#xff08;新浪微博机器学习团队AI Lab负责人&#xff09;编辑 | Jane出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;【导读】BERT提出的这一年&#xff0c;也是NLP领域迅速发展的一年。学界不断提出新的预训练模型&#xff0c;刷新各…

Eclipse创建web工程时,报错Dynamic Web Module 3.0 requires Java 1.6 or newer.

报错&#xff1a; 解决方案&#xff1a; 1.打开eclipse工具栏window->preferences 2.打开java->compiler 3.选择compiler compliance level在1.6以上版本&#xff08;此处选择1.8&#xff09; 4.点击apply and close保存修改&#xff0c;即可 转载于:https://www.cnblogs…

Maven学习总结(八)——使用Maven构建多模块项目

2019独角兽企业重金招聘Python工程师标准>>> Maven学习总结(八)——使用Maven构建多模块项目 在平时的Javaweb项目开发中为了便于后期的维护&#xff0c;我们一般会进行分层开发&#xff0c;最常见的就是分为domain&#xff08;域模型层&#xff09;、dao&#xff0…

哈工大、清华、CSDN、嵌入式视觉联盟合办的 AIoT 盛会,你怎么舍得错过?!

2019 嵌入式智能国际大会即将来袭&#xff01;随着物联网和人工智能技术的飞速发展与相互渗透&#xff0c;万物智联的新赛道已经开始显现。据中商产业研究院《2016—2021年中国物联网产业市场研究报告》显示&#xff0c;预计到2020年&#xff0c;中国物联网的整体规模将达2.2万…

OpenCV3.3中主成分分析(Principal Components Analysis, PCA)接口简介及使用

OpenCV3.3中给出了主成分分析(Principal Components Analysis, PCA)的实现&#xff0c;即cv::PCA类&#xff0c;类的声明在include/opencv2/core.hpp文件中&#xff0c;实现在modules/core/src/pca.cpp文件中,其中&#xff1a;(1)、cv::PCA::PCA&#xff1a;构造函数&#xff1…

Spring MVC配置

为什么80%的码农都做不了架构师&#xff1f;>>> 一、传统方式配置Spring MVC &#xff08;1&#xff09;导入jar包 需要导入如下的jar包 junit-3.8.1.jar spring-core-3.0.5.RELEASE.jar commons-logging-1.1.1.jar spring-context-3.0.5.REL…

主成分分析(PCA)Python代码实现

主成分分析(Principal Components Analysis, PCA)简介可以参考&#xff1a; http://blog.csdn.net/fengbingchun/article/details/78977202 这里是参照 http://sebastianraschka.com/Articles/2014_pca_step_by_step.html 文章中的code整理的Python代码&#xff0c;实现过程为…

AI发展这一年:不断衍生的技术丑闻与抵制声潮

作者 | AI Now学院译者 | Raku编辑 | Jane出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】10月2日&#xff0c;纽约大学AI Now学院在纽约大学斯克博剧院&#xff08;Skirball Theatre&#xff09;组织召开了第四届年度AI Now研讨会。研讨会邀请了业内组织者…

Distributed Configuration Management Platform(分布式配置管理平台)

2019独角兽企业重金招聘Python工程师标准>>> 专注于各种 分布式系统配置管理 的通用组件/通用平台, 提供统一的配置管理服务。 主要目标&#xff1a; 部署极其简单&#xff1a;同一个上线包&#xff0c;无须改动配置&#xff0c;即可在 多个环境中(RD/QA/PRODUCTION…

如何利用zendstudio新建 或导入php项目

为什么80%的码农都做不了架构师&#xff1f;>>> 一、利用ZendStudio创建 PHP Project 1. 打开ZendStudio, 选择&#xff1a;File &agrave; New &agrave; PHP Project, 如下图所示: 于是弹出如下界面&#xff1a; 在”Project name”后输入工程名(比如我这里…

一文读懂GoogLeNet神经网络 | CSDN博文精选

作者 | .NY&XX来源 | CSDN博客本文介绍的是著名的网络结构GoogLeNet&#xff0c;目的是试图领会其中结构设计思想。GoogLeNet特点优化网络质量的生物学原理GoogLeNet网络结构的动机GoogLeNet架构细节Inception模块和普通卷积结构的差异辅助分类器GoogLeNet网络架构GoogLeNe…

C++中的函数签名

C中的函数签名(function signature)&#xff1a;包含了一个函数的信息&#xff0c;包括函数名、参数类型、参数个数、顺序以及它所在的类和命名空间。普通函数签名并不包含函数返回值部分&#xff0c;如果两个函数仅仅只有函数返回值不同&#xff0c;那么系统是无法区分这两个函…

MyEclipse断点调试

2019独角兽企业重金招聘Python工程师标准>>> 1、在编辑的程序的左边&#xff0c;你会看到一条浅浅的灰色编带&#xff0c;在这里设置断点。 2、设置断点的方法有很多 方法&#xff1a;1&#xff09;、双击 &#xff1b; 2&#xff09;、右键&#xff0c;选择“Toggl…

C primer plus 练习题 第三章

5. 1 #include <stdio.h>2 3 int main()4 {5 float you_sec;6 printf("请输入你的年龄:");7 scanf("%f", &you_sec);8 printf("年龄合计:%e 秒!\n", you_sec * 3.156e7);9 getchar(); 10 return 0; 11 }

Echache整合Spring缓存实例讲解

2019独角兽企业重金招聘Python工程师标准>>> 摘要&#xff1a;本文主要介绍了EhCache&#xff0c;并通过整合Spring给出了一个使用实例。 一、EhCache 介绍 EhCache 是一个纯Java的进程内缓存框架&#xff0c;具有快速、精干等特点&#xff0c;是Hibernate中默认的C…

灰度图像直方图均衡化公式及实现

图像的直方图&#xff1a;直方图是图像中像素强度分布的图形表达方式。它统计了每一个强度值所具有的像素个数。直方图均衡化&#xff1a;是通过拉伸像素强度分布范围来增强图像对比度的一种方法。是图像处理领域中利用图像直方图对对比度进行调整的方法。均衡化指的是把一个分…

超越最新无监督域自适应方法,研究人员提轻量CNN新架构OSNet

作者 | Kaiyang Zhou, Xiatian Zhu, Yongxin Yang, Andrea Cavallaro, and Tao Xiang 译者 | TroyChang 编辑 | Jane 出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09; CNN新架构OSNet 【导读】今天推荐论文《Learning Generalisable Omni-Scale Repre…

在一台机器上搭建多个redis实例

2019独角兽企业重金招聘Python工程师标准>>> 默认Redis程序安装在/usr/local/redis目录下&#xff1b; 配置文件&#xff1a;/usr/local/redis/redis.conf&#xff0c;该配置文件中配置的端口为默认端口&#xff1a;6379&#xff1b; Redis的启动命令路径&#xff1…

使用kaptcha生成验证码

2019独角兽企业重金招聘Python工程师标准>>> kaptcha是一个简单好用的验证码生成工具&#xff0c;通过配置&#xff0c;可以自己定义验证码大小、颜色、显示的字符等等。下面就来讲一下如何使用kaptcha生成验证码以及在服务器端取出验证码进行校验。 一、搭建测试环…

主成分分析(PCA) C++ 实现

主成分分析(Principal Components Analysis, PCA)简介可以参考&#xff1a; http://blog.csdn.net/fengbingchun/article/details/78977202以下是PCA的C实现&#xff0c;参考OpenCV 3.3中的cv::PCA类。使用ORL Faces Database作为测试图像。关于ORL Faces Database的介绍可以参…

为何Google、微软、华为将亿级源代码放一个仓库?从全球最大代码管理库说起...

作者 | 夕颜编辑 | Just出品 | AI 科技大本营&#xff08;ID:rgznai100&#xff09;【导读】2017 年&#xff0c;在当时微软的一篇官方博客中&#xff0c;时任微软云开发服务副总裁的 Brian Harry 表示微软内部代码开始向 Git 迁移&#xff0c;宣布推出针对大规模 repo 的“Git…

jquery mobie导致超链接不可用

在a标签中添加rel"external"即可转载于:https://blog.51cto.com/here2142/1435434

编译器GCC与Clang的异同

GCC&#xff1a;GNU(Gnus Not Unix)编译器套装(GNU Compiler Collection&#xff0c;GCC)&#xff0c;指一套编程语言编译器&#xff0c;以GPL及LGPL许可证所发行的自由软件&#xff0c;也是GNU项目的关键部分&#xff0c;也是GNU工具链的主要组成部分之一。GCC(特别是其中的C语…