详解隐马尔可夫模型(HMM)中的维特比算法
笔记转载于GitHub项目:https://github.com/NLP-LOVE/Introduction-NLP
4. 隐马尔可夫模型与序列标注
第3章的n元语法模型从词语接续的流畅度出发,为全切分词网中的二元接续打分,进而利用维特比算法求解似然概率最大的路径。这种词语级别的模型无法应对 OOV(Out of Vocabulary,即未登录词) 问题: 00V在最初的全切分阶段就已经不可能进人词网了,更何谈召回。
例如下面一句:
头上戴着束发嵌宝紫金冠,齐眉勒着二龙抢珠金抹额
加粗的就是相对陌生的新词,之前的分词算法识别不出,但人类确可以,是因为读者能够识别“戴着”,这些构词法能让人类拥有动态组词的能力。我们需要更细粒度的模型,比词语更细粒度的就是字符。
具体说来,只要将每个汉字组词时所处的位置(首尾等)作为标签,则中文分词就转化为给定汉字序列找出标签序列的问题。一般而言,由字构词是序列标注模型的一种应用。 在所有“序列标注”模型中,隐马尔可夫模型是最基础的一种。
4.1 序列标注问题
序列标注指的是给定一个序列 \(x=x_1x_2...x_n\),找出序列中每个元素对应标签 \(y=y_1y_2...y_n\) 的问题。其中,y 所有可能的取值集合称为标注集。比如,输入一个自然数序列,输出它们的奇偶性。
求解序列标注问题的模型一般称为序列标注器,通常由模型从一个标注数据集 \(\{X,Y\}=\{(x^{(i)},y^{(i)})\},i=1,...,K\) 中学习相关知识后再进行预测。再NLP问题中,x 通常是字符或词语,而 y 则是待预测的组词角色或词性等标签。中文分词、词性标注以及命名实体识别,都可以转化为序列标注问题。
序列标注与中文分词
考虑一个字符序列(字符串) x,想象切词器真的是在拿刀切割字符串,如此,中文分词转化为标注集{切,过}的序列标注问题。
分词标注集并非只有一种,为了捕捉汉字分别作为词语收尾(Begin、End)、词中(Middle)以及单字成词(Single)时不同的成词概率,人们提出了{B,M,E,S}这种最流行的标注集。
序列标注与词性标注
词性标注任务是一个天然的序列标注问题:x 是单词序列,y 是相应的词性序列。需要综合考虑前后的单词与词性才能决定当前单词的词性。
序列标注与命名实体识别
所谓命名实体,指的是现实存在的实体,比如人名、地名和机构名,命名实体是 OOV 的主要组成部分。
考虑到字符级别中文分词和词语级别命名实体识别有着类似的特点,都是组合短单位形成长单位的问题。所以命名实体识别可以复用BMES标注集,并沿用中文分词的逻辑,只不过标注的对象由字符变为单词而已。唯一不同的是,命名实体识别还需要确定实体所属的类别。这个额外的要求依然是个标注问题,可以通过将命名实体类别附着到BMES标签来达到目的。比如,构成地名的单词标注为“B/M/E/S-地名”,以此类推。对于那些不构成命名实体的单词,则统-标注为O ( Outside), 即复合词之外。
总之,序列标注问题是NLP中最常见的问题之一。许多应用任务都可以变换思路,转化为序列标注来解决。所以一个准确的序列标注模型非常重要,直接关系到NLP系统的准确率。机器学习领域为NLP提供了许多标注模型,本着循序渐进的原则,本章介绍其中最基础的一个隐马尔可夫模型。
4.2 隐马尔可夫模型
隐马尔可夫模型( Hidden Markov Model, HMM)是描述两个时序序列联合分布 p(x,y) 的概率模型: x 序列外界可见(外界指的是观测者),称为观测序列(obsevation sequence); y 序列外界不可见,称为状态序列(state sequence)。比如观测 x 为单词,状态 y 为词性,我们需要根据单词序列去猜测它们的词性。隐马尔可夫模型之所以称为“隐”,是因为从外界来看,状
态序列(例如词性)隐藏不可见,是待求的因变量。从这个角度来讲,人们也称状态为隐状态(hidden state),而称观测为显状态( visible state)。隐马尔可夫模型之所以称为“马尔可夫模型”,”是因为它满足马尔可夫假设。
从马尔可夫假设到隐马尔可夫模型
马尔可夫假设:每个事件的发生概率只取决于前一个事件。
马尔可夫链:将满足马尔可夫假设的连续多个事件串联起来,就构成了马尔可夫链。
如果把事件具象为单词,那么马尔可夫模型就具象为二元语法模型。
隐马尔可夫模型:它的马尔可夫假设作用于状态序列,
假设 ① 当前状态 Yt 仅仅依赖于前一个状态 Yt-1, 连续多个状态构成隐马尔可夫链 y。有了隐马尔可夫链,如何与观测序列 x 建立联系呢?
隐马尔可夫模型做了第二个假设: ② 任意时刻的观测 x 只依赖于该时刻的状态 Yt,与其他时刻的状态或观测独立无关。如果用箭头表示事件的依赖关系(箭头终点是结果,依赖于起点的因缘),则隐马尔可夫模型可以表示为下图所示
状态与观测之间的依赖关系确定之后,隐马尔可夫模型利用三个要素来模拟时序序列的发生过程----即初始状态概率向量、状态转移概率矩阵和发射概率矩阵。
初始状态概率向量
系统启动时进入的第一个状态 Y1 称为初始状态,假设 y 有 N 种可能的取值,那么 Y1 就是一个独立的离散型随机变量,由 P(y1 | π) 描述。其中
\[\pi=\left(\pi_{1}, \cdots, \pi_{N}\right)^{\mathrm{T}}, 0 \leqslant \pi_{i} \leqslant 1, \sum_{i=1}^{N} \pi_{i}=1
\]是概率分布的参数向量,称为初始状态概率向量。
给定 π ,初始状态 Y1 的取值分布就确定了,比如采用{B,M,E,S}标注集时概率如下:
\[p(y_1=B)=0.7\\
p(y_1=M)=0\\
p(y_1=E)=0\\
p(y_1=S)=0.3
\]那么此时隐马尔可夫模型的初始状态概率向量为 π=[0.7,0,0,0.3],注意,句子第一个词是单字的可能性要小一些。
状态转移矩阵
Yt 如何转移到 Yt+1 呢?根据马尔可夫假设,t+1 时的状态仅仅取决于 t 时的状态,既然一共有 N 种状态,那么从状态 Si 到状态 Sj 的概率就构成了一个 N*N 的方阵,称为状态转移矩阵 A:
\[A=\left[p\left(y_{t+1}=s_{j} | y_{t}=s_{i}\right)\right]_{N \times N}
\]其中下标 i、j 分别表示状态的第 i、j 种取值。状态转移概率的存在有其实际意义,在中文分词中,标签 B 的后面不可能是 S,于是就有 P(Yt+1 = S | Yt = B) = 0。同样,词性标注中的“形容词->名词”“副词->动词”也可通过状态转移概率来模拟,这些概率分布参数不需要手动设置,而是通过语料库上的统计自动学习。
发射概率矩阵
有了状态 Yt 之后,如何确定观测 Xt 的概率分布呢?根据隐马尔可夫假设②,当前观测 Xt 仅仅取决于当前状态 Yt。也就是说,给定每种 y,x 都是一个独立的离散型随机变量,其参数对应一个向量。 假设观测 x 一共有 M 种可能的取值,则 x 的概率分布参数向量维度为 M。由于 y 共有 N 种,所以这些参数向量构成了 N*M 的矩阵,称为发射概率矩阵B。
\(\boldsymbol{B}=\left[p\left(x_{t}=o_{i} | y_{t}=s_{j}\right)\right]_{N \times M}\)
其中,第 i 行 j 列的元素下标 i 和 j 分别代表观测和状态的第 i 种和第 j 种取值。
隐马尔可夫模型的三个基本用法
样本生成问题:给定模型,如何有效计算产生观测序列的概率?换言之,如何评估模型与观测序列之间的匹配程度?
序列预测问题:给定模型和观测序列,如何找到与此观测序列最匹配的状态序列?换言之,如何根据观测序列推断出隐藏的模型状态?
模型训练问题:给定观测序列,如何调整模型参数使得该序列出现的概率最大?换言之,如何训练模型使其能最好地描述观测数据?
前两个问题是模式识别的问题:1) 根据隐马尔科夫模型得到一个可观察状态序列的概率(评价);2) 找到一个隐藏状态的序列使得这个序列产生一个可观察状态序列的概率最大(解码)。第三个问题就是根据一个可以观察到的状态序列集产生一个隐马尔科夫模型(学习)。
4.3 隐马尔可夫模型的训练
案例假设和模型构造
设想如下案例:某医院招标开发“智能”医疗诊断系统,用来辅助感冒诊断。已知①来诊者只有两种状态:要么健康,要么发烧。②来诊者不确定自己到底是哪种状态,只能回答感觉头晕、体寒或正常。医院认为,③感冒这种病,只跟病人前一天的状态有关,并且,④当天的病情决定当天的身体感觉。有位来诊者的病历卡上完整地记录了最近 T 天的身体感受(头晕、体寒或正常),请预测这 T 天的身体状态(健康或发烧)。由于医疗数据属于机密隐私,医院无法提供训练数据,但根据医生经验,感冒发病的规律如下图所示(箭头上的数值表示概率):
根据已知条件①②,病情状态(健康、发烧)可作为隐马尔可夫模型的隐状态(上图蓝色状态),而身体感受(头晕、体寒或正常)可作为隐马尔可夫模型的显状态(图中白色状态)。条件③符合隐马尔可夫模型假设一,条件④符 合隐马尔可夫模型假设二。这个案例其实描述了一个隐马尔可夫模型, 并且参数已经给定。构造模型代码见:
import numpy as np
from pyhanlp import *
from jpype import JArray, JFloat, JInt to_str = JClass('java.util.Arrays').toString ## 隐马尔可夫模型描述
states = ('Healthy', 'Fever')
start_probability = {'Healthy': 0.6, 'Fever': 0.4}
transition_probability = {
'Healthy': {'Healthy': 0.7, 'Fever': 0.3},
'Fever': {'Healthy': 0.4, 'Fever': 0.6},
}
emission_probability = {
'Healthy': {'normal': 0.5, 'cold': 0.4, 'dizzy': 0.1},
'Fever': {'normal': 0.1, 'cold': 0.3, 'dizzy': 0.6},
}
observations = ('normal', 'cold', 'dizzy') def generate_index_map(lables):
index_label = {}
label_index = {}
i = 0
for l in lables:
index_label[i] = l
label_index[l] = i
i += 1
return label_index, index_label states_label_index, states_index_label = generate_index_map(states)
observations_label_index, observations_index_label = generate_index_map(observations) def convert_map_to_matrix(map, label_index1, label_index2):
m = np.empty((len(label_index1), len(label_index2)), dtype=float)
for line in map:
for col in map[line]:
m[label_index1[line]][label_index2[col]] = map[line][col]
return JArray(JFloat, m.ndim)(m.tolist()) def convert_observations_to_index(observations, label_index):
list = []
for o in observations:
list.append(label_index[o])
return list def convert_map_to_vector(map, label_index):
v = np.empty(len(map), dtype=float)
for e in map:
v[label_index[e]] = map[e]
return JArray(JFloat, v.ndim)(v.tolist()) # 将numpy数组转为Java数组 ## pi:初始状态概率向量
## A:状态转移概率矩阵
## B:发射概率矩阵
A = convert_map_to_matrix(transition_probability, states_label_index, states_label_index)
B = convert_map_to_matrix(emission_probability, states_label_index, observations_label_index)
observations_index = convert_observations_to_index(observations, observations_label_index)
pi = convert_map_to_vector(start_probability, states_label_index) FirstOrderHiddenMarkovModel = JClass('com.hankcs.hanlp.model.hmm.FirstOrderHiddenMarkovModel')
given_model = FirstOrderHiddenMarkovModel(pi, A, B)
样本生成算法
它的生成过程就是沿着隐马尔可夫链走 T 步:
- 根据初始状态概率向量采样第一个时刻的状态 Y1 = Si,即 Y1 ~ π。
- Yt 采样结束得到 Si 后,根据状态转移概率矩s阵第 i 行的概率向量,采样下一时刻的状态 Yt+1。
- 对每个 Yt = Si,根据发射概率矩阵的第 i 行采样 Xt。
- 重复步骤 2 共计 T-1 次,重复步骤 3 共计 T 次,输出序列 x 与 y。
代码如下(接上),直接通过模型进行生成:
## 第一个参数:序列最低长度
## 第二个参数:序列最高长度
## 第三个参数:需要生成的样本数
for O, S in given_model.generate(3, 5, 2):
print(" ".join((observations_index_label[o] + '/' + states_index_label[s]) for o, s in zip(O, S)))
隐马尔可夫模型的训练
样本生成后,我们就可以利用生成的数据重新训练,通过极大似然法来估计隐马尔可夫模型的参数。参数指的是三元组(π,A,B)。
利用给定的隐马尔可夫模型 P生成十万个样本,在这十万个样本上训练新模型Q,比较新旧模型参数是否一致。
trained_model = FirstOrderHiddenMarkovModel() ## 第一个参数:序列最低长度
## 第二个参数:序列最高长度
## 第三个参数:需要生成的样本数
trained_model.train(given_model.generate(3, 10, 100000))
print('新模型与旧模型是否相同:', trained_model.similar(given_model))
输出:
新模型与旧模型是否相同: True
运行后一般都成立,由于随机数,仅有小概率发生失败。
4.4 隐马尔可夫模型的预测
隐马尔可夫模型最具实际意义的问题当属序列标注了:给定观测序列,求解最可能的状态序列及其概率。
概率计算的前向算法
给定观测序列 x 和一个状态序列 y,就可以估计两者的联合概率 P(x,y),联合概率就是一种结果的概率,在这些结果当中找到最大的联合概率就是找到最有可能的结果预测。联合概率:P(x,y) = P(y) P(x|y),下面我们来分别求出P(y)和P(x|y)
顺着隐马尔可夫链走,首先 t=1 时初始状态没有前驱状态,发生概率由 π 决定:
\[P(y_1=s_i)=\pi_i
\]接着对 t >= 2,状态 Yt 由前驱状态 Yt-1 转移而来,转移矩阵由矩阵 A 决定:
\[P(y_t=s_j|y_{t-1}=s_i)=A_{i,j}
\]所以状态序列的概率为上面式子的乘积:
\[p(y)=p\left(y_{1}, \cdots, y_{r}\right)=p\left(y_{1}\right) \prod_{i=2}^{T} p\left(y_{i} | y_{i-1}\right)
\]P(y) 我们已经求出来了,下面要求 P(x|y)
对于每个 Yt = Si,都会“发射”一个 Xt = Oj,发射概率由矩阵 B 决定:
\[P(x_t=O_j|y_t=s_i)=B_{i,j}
\]那么给定一个状态序列 Y,对应的 X 的概率累积形式:
\[p(x | y)=\prod_{t=1}^{T} p\left(x_{t} | y_{t}\right)
\]最后带入联合概率公式得:
\[\begin{aligned}
p(x, y) &=p(y) p(x | y) \\
&=p\left(y_{1}\right) \prod_{t=2}^{T} p\left(y_{t} | y_{t-1}\right) \prod_{t=1}^{T} p\left(x_{t} | y_{t}\right)
\end{aligned}
\]
将其中的每个 Xt、Yt 对应上实际发生序列的 Si、Oj,就能带入(π,A,B)中的相应元素,从而计算出任意序列的概率,最后找出这些概率的最大值就得到预测结果。找出概率最大值要用到维特比算法。
搜索状态序列的维特比算法
理解了前向算法之后,找寻最大概率所对应的状态序列无非是一个搜索问题。具体说来,将每个状态作为有向图中的一个节点, 节点间的距离由转移概率决定,节点本身的花费由发射概率决定。那么所有备选状态构成一幅有 向无环图,待求的概率最大的状态序列就是图中的最长路径,此时的搜索算法称为维特比算法,如图下图所示:
上图从左往右时序递增,虚线由初始状态概率决定,实线则是转移概率。由于受到观测序列的约束,不同状态发射观测的概率不同,所以每个节点本身也必须计算自己的花费,由发射概率决定。又由于 Yt+1 仅依赖于 Yt,所以网状图可以动态规划的搜索,也就是维特比算法:
初始化,t=1 时初始最优路径的备选由 N 个状态组成,它们的前驱为空。
\[\begin{aligned}
\delta_{1, i}=\pi_{i} \boldsymbol{B}_{i, o_{1}}, & i=1, \cdots, N \\
\psi_{1, i}=0, & i=1, \cdots, N
\end{aligned}
\]其中,δ 存储在时刻 t 以 Si 结尾的所有局部路径的最大概率。ψ 存储局部最优路径末状态 Yt 的前驱状态。
递推,t >= 2 时每条备选路径像贪吃蛇一样吃入一个新状态,长度增加一个单位,根据转移概率和发射概率计算花费。找出新的局部最优路径,更新 δ、ψ 两个数组。
\[\begin{array}{ll}
{\delta_{t, i}=\max _{1 \leqslant j \leqslant N}\left(\delta_{t-1, j} A_{j, i}\right) B_{i, o_{t}},} & {i=1, \cdots, N} \\
{\psi_{t, i}=\arg \max _{1 \leqslant j \leqslant N}\left(\delta_{t-1, j} A_{j, i}\right),} & {i=1, \cdots, N}
\end{array}
\]终止,找出最终时刻 δt,i 数组中的最大概率 P*,以及相应的结尾状态下标 i*t。
\[\begin{aligned}
&p^{*}=\max _{1 \leqslant i \leqslant N} \delta_{T, i}\\
&i_{T}^{*}=\arg \max _{1 \leqslant i \leqslant N} \delta_{T, i}
\end{aligned}
\]回溯,根据前驱数组 ψ 回溯前驱状态,取得最优路径状态下标。
\[i_{t}^{*}=\psi_{t+1, i_{t+1}}, \quad t=T-1, T-2, \cdots, 1
\]
预测代码如下(接上面代码):
pred = JArray(JInt, 1)([0, 0, 0])
prob = given_model.predict(observations_index, pred)
print(" ".join((observations_index_label[o] + '/' + states_index_label[s]) for o, s in
zip(observations_index, pred)) + " {:.3f}".format(np.math.exp(prob)))
输出为:
normal/Healthy cold/Healthy dizzy/Fever 0.015
观察该结果,“/”隔开观测和状态,最后的 0.015 是序列的联合概率。
4.5 隐马尔可夫模型应用于中文分词
HanLP 已经实现了基于隐马尔可夫模型的中文分词器 HMMSegmenter,并且实现了训练接口。代码详见:
hmm_cws.py:https://github.com/NLP-LOVE/Introduction-NLP/tree/master/code/ch04/hmm_cws.py
4.6 性能评测
如果隐马尔可夫模型中每个状态仅依赖于前一个状态, 则称为一阶;如果依赖于前两个状态,则称为二阶。既然一阶隐马尔可夫模型过于简单,是否可以切换到二阶来提高分数呢?
答案是可以的,跟一阶类似,这里不再详细介绍二阶隐马尔可夫模型,详细请看原书。
这里我们使用 MSR语料库进行评测,结果如下表所示:
算法 | P | R | F1 | R(oov) | R(IV) |
---|---|---|---|---|---|
最长匹配 | 89.41 | 94.64 | 91.95 | 2.58 | 97.14 |
二元语法 | 92.38 | 96.70 | 94.49 | 2.58 | 99.26 |
一阶HHM | 78.49 | 80.38 | 79.42 | 41.11 | 81.44 |
二阶HHM | 78.34 | 80.01 | 79.16 | 42.06 | 81.04 |
可以看到,二阶隐马尔可夫模型的 Roov 有少许提升,但综合 F1 反而下降了。这说明增加隐马尔可夫模型的阶数并不能提高分词器的准确率,单靠提高转移概率矩阵的复杂度并不能提高模型的拟合能力,我们需要从别的方面想办法。目前市面上一些开源分词器仍然停留在一阶隐马尔可夫模型的水平,比如著名的结巴分词,它们的准确率也只能达到80%左右。
4.7 总结
这一章我们想解决的问题是新词识别,为此从词语级模型切换到字符级模型,将中文分词任务转换为序列标注问题。作为新手起步,我们尝试了最简单的序列标注模型----隐马尔可夫模型。隐马尔可夫模型的基本问题有三个:样本生成、参数估计、序列预测。
然而隐马尔可夫模型用于中文分词的效果并不理想,虽然召回了一半的 OOV,但综合 F1 甚至低于词典分词。哪怕升级到二阶隐马尔可夫模型, F1 值依然没有提升。 看来朴素的隐马尔可夫模型不适合中文分词,我们需要更高级的模型。
话说回来,隐马尔可夫模型作为入门模型,比较容易上手,同时也是许多高级模型的基础。打好基础,我们才能挑战高级模型。
4.8 GitHub项目
HanLP何晗--《自然语言处理入门》笔记:
https://github.com/NLP-LOVE/Introduction-NLP
项目持续更新中......
目录
章节 |
---|
第 1 章:新手上路 |
第 2 章:词典分词 |
第 3 章:二元语法与中文分词 |
第 4 章:隐马尔可夫模型与序列标注 |
第 5 章:感知机分类与序列标注 |
第 6 章:条件随机场与序列标注 |
第 7 章:词性标注 |
第 8 章:命名实体识别 |
第 9 章:信息抽取 |
第 10 章:文本聚类 |
第 11 章:文本分类 |
第 12 章:依存句法分析 |
第 13 章:深度学习与自然语言处理 |
详解隐马尔可夫模型(HMM)中的维特比算法的更多相关文章
- 基于隐马尔科夫模型(HMM)的地图匹配(Map-Matching)算法
文章目录 1. 1. 摘要 2. 2. Map-Matching(MM)问题 3. 3. 隐马尔科夫模型(HMM) 3.1. 3.1. HMM简述 3.2. 3.2. 基于HMM的Map-Matchi ...
- 猪猪的机器学习笔记(十七)隐马尔科夫模型HMM
隐马尔科夫模型HMM 作者:樱花猪 摘要: 本文为七月算法(julyedu.com)12月机器学习第十七次课在线笔记.隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来 ...
- 机器学习之隐马尔科夫模型HMM(六)
摘要 隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔科夫过程.其难点是从可观察的参数中确定该过程的隐含参数,然后利用这些参数来作进一步 ...
- 隐马尔科夫模型(HMM)
基本概念 1Markov Models 2Hidden Markov Models 3概率计算算法前向后向算法 1-3-1直接计算 1-3-2前向算法 1-3-3后向算法 4学习问题Baum-Welc ...
- 隐马尔科夫模型HMM学习最佳范例
谷歌路过这个专门介绍HMM及其相关算法的主页:http://rrurl.cn/vAgKhh 里面图文并茂动感十足,写得通俗易懂,可以说是介绍HMM很好的范例了.一个名为52nlp的博主(google ...
- 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率
隐马尔科夫模型HMM(一)HMM模型 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数(TODO) 隐马尔科夫模型HMM(四)维特比算法 ...
- 隐马尔科夫模型HMM(一)HMM模型
隐马尔科夫模型HMM(一)HMM模型基础 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数(TODO) 隐马尔科夫模型HMM(四)维特比 ...
- 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数
隐马尔科夫模型HMM(一)HMM模型 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数(TODO) 隐马尔科夫模型HMM(四)维特比算法 ...
- 隐马尔科夫模型HMM(四)维特比算法解码隐藏状态序列
隐马尔科夫模型HMM(一)HMM模型 隐马尔科夫模型HMM(二)前向后向算法评估观察序列概率 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数 隐马尔科夫模型HMM(四)维特比算法解码隐藏状态 ...
随机推荐
- 轻松搭建基于 SpringBoot + Vue 的 Web 商城应用
背景介绍 首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传.函数 ...
- 机器学习之路--KNN算法
机器学习实战之kNN算法 机器学习实战这本书是基于python的,如果我们想要完成python开发,那么python的开发环境必不可少: (1)python3.52,64位,这是我用的python ...
- 「BZOJ4590」「SHOI2015」 自动刷题机 解题报告
自动刷题机 Description 曾经发明了信号增幅仪的发明家SHTSC又公开了他的新发明:自动刷题机--一种可以自动AC题目的神秘装置.自动刷题机刷题的方式非常简单:首先会瞬间得出题目的正确做法, ...
- 1072 开学寄语 (20分)C语言
下图是上海某校的新学期开学寄语:天将降大任于斯人也,必先删其微博,卸其 QQ,封其电脑,夺其手机,收其 ipad,断其 wifi,使其百无聊赖,然后,净面.理发.整衣,然后思过.读书.锻炼.明智.开悟 ...
- java基础之----分布式事务tcc
最近研究了一下分布式事务框架,ttc,总体感觉还可以,当然前提条件下是你要会使用这个框架.下面分层次讲,尽量让想学习的同学读了这篇文章能加以操作运用.我不想废话,直接上干货. 一.什么是tcc?干什么 ...
- Win10系统下应用窗口任务栏居中效果
实现步骤: 在资源管理器中新建文件夹,一定要保证文件夹内无任何文件 任务栏上鼠标右键,移动到工具栏上,选择新建工具栏 选择新建的空文件夹 空文件夹出现在任务栏后,鼠标可以拖动工具栏前的两条竖线(图片上 ...
- 【转】线性插值(Linear Interpolation)基本原理
转:https://blog.csdn.net/u010312937/article/details/82055431 今天在阅读大牛代码的时候,发现了Linear Interpolation一次,百 ...
- Linux.vim.多行复制、删除、剪切
复制: //单行复制+粘贴 yy + p:复制光标所处当前行, 敲p粘贴在光标处. //多行复制+粘贴 n + yy + p:复制光标所在行起以下n行(含当前行), 敲yy复制光标所处当前行, 敲p粘 ...
- Entity Framework 6+ 连接Mysql
好吧.这个博客开不开的 我感觉.. 都一样了. 前言: 公司改造Sqlserver ->Mysql Sql2016老夫对不住你啊.. 好 前沿结束. 需要的家伙: 1.mysql-for-vis ...
- 玩转Django2.0---Django笔记建站基础五(模板)
第五章 模板 Django作为web框架,需要一种很便利的方法去动态地生成HTML网页,因此有了模板这个概念.模板包含所需HTML的部分代码以及一些特殊语法 Django可以配置一个或多个模板引擎(甚 ...