精华内容
下载资源
问答
  • 条件随机场(CRF)

    万次阅读 多人点赞 2018-08-24 16:58:27
    当时全网搜中文资料,陆续失望地发现竟然真的没有讲得清楚的博文,发现基本是把李航老师书里或CRF tutorial等资料的文字论述公式抄来抄去的。当然,没有说别人讲的是错的,只是觉得,要是没有把东西说的让读者看...

    作者:Scofield
    链接:https://www.zhihu.com/question/35866596/answer/236886066
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
     

    so far till now, 我还没见到过将CRF讲的个明明白白的。一个都没。就不能不抄来抄去吗?
    我打算搞一个这样的版本,无门槛理解的。
    ——20170927

    陆陆续续把调研学习工作完成了,虽然历时有点久,现在put上来。评论里的同学也等不及了时不时催我,所以不敢怠慢啊……

    总结的还算比较体系化,蛮长的,请读者慢慢看,肯定有收获的。

    (好痛苦,这么多公式都要在知乎上重输;是在MD上写的,在知乎上没想到格式这么难看……)

    ——20180129


    概率图模型学习笔记:HMM、MEMM、CRF


    一、Preface

    二、Prerequisite

    2.1 概率图

    2.1.1 概览
    2.1.2 有向图 vs. 无向图
    2.1.3 马尔科夫假设&马尔科夫性
    2.2 判别式模型 vs. 生成式模型
    2.3 序列建模

    三、HMM

    3.1 理解HMM
    3.2 模型运行过程
    3.2.1 学习过程
    3.2.2 序列标注(解码)过程
    3.2.3 序列概率过程

    四、MEMM

    4.1 理解MEMM
    4.2 模型运行过程

    4.2.1 学习过程
    4.2.2 序列标注(解码)过程
    4.2.3 序列概率过程
    4.3 标注偏置?

    五、CRF

    5.1 理解CRF
    5.2 模型运行过程
    5.2.1 学习过程
    5.2.2 解码过程
    5.2.3 序列概率过程
    5.3 CRF++分析
    5.4 LSTM+CRF

    六、总结

    Referrence


    一、Preface

    之前刚接触NLP时做相关的任务,也必然地涉及到了序列处理任务,然后自然要接触到概率图模型。当时在全网搜中文资料,陆续失望地发现竟然真的没有讲得清楚的博文,发现基本是把李航老师书里或CRF tutorial等资料的文字论述和公式抄来抄去的。当然,没有说别人讲的是错的,只是觉得,要是没有把东西说的让读者看得懂,那也是没意义啊。或者有些吧,就是讲了一大堆的东西,貌似也明白了啥,但还是不能让我很好的理解CRF这些模型究竟是个啥,完了还是有一头雾水散不开的感觉。试想,一堆公式扔过来,没有个感性理解的过渡,怎么可能理解的了。我甚至觉得,如果博客让人看不懂,那说明要么自己没理解透要么就是思维不清晰讲不清楚。所以默想,深水区攻坚还是要靠自己,然后去做调研做research,所以就写了个这个学习记录。

    所以概率图的研究学习思考列入了我的任务清单。不过平时的时间又非常的紧,只能陆陆续续的思考着,所以时间拖得也真是长啊。

    这是个学习笔记。相比其他的学习模型,概率图貌似确实是比较难以理解的。这里我基本全部用自己的理解加上自己的语言习惯表达出来,off the official form,表达尽量接地气。我会尽量将我所有理解过程中的每个关键小细节都详细描述出来,以使对零基础的初学者友好。包括理论的来龙去脉,抽象具象化,模型的构成,模型的训练过程,会注重类比的学习。

    根据现有资料,我是按照概率图模型将HMM,MEMM,CRF放在这里一起对比学习。之所以把他们拿在一起,是因为他们都用于标注问题。并且之所以放在概率图框架下,是完全因为自己top-down思维模式使然。另外,概率图下还有很多的模型,这儿只学习标注模型。

    正儿八经的,我对这些个概率图模型有了彻悟,是从我明白了生成式模型与判别式模型的那一刻。一直在思考从概率图模型角度讲他们的区别到底在哪。

    另外,篇幅略显长,但咱们不要急躁,好好看完这篇具有良好的上下文的笔记,那肯定是能理解的,或者就多看几遍。

    个人学习习惯就是,要尽可能地将一群没有结构的知识点融会贯通,再用一条树状结构的绳将之串起来,结构化,就是说要成体系,这样把绳子头一拎所有的东西都能拿起来。学习嘛,应该要是一个熵减的过程,卓有成效的学习应该是混乱度越来越小!这个思维方式对我影响还是蛮大的。

    在正式内容之前,还是先要明确下面这一点,最好脑子里形成一个定势:

    统计机器学习所有的模型(个别instant model和优化算法以及其他的特种工程知识点除外)的工作流程都是如此:
    a.训练模型参数,得到模型(由参数唯一确定),
    b.预测给定的测试数据。
    拿这个流程去挨个学习模型,思路上会非常顺畅。这一点可参见我另一篇文字介绍。

    除此之外,对初学者的关于机器学习的入门学习方式也顺带表达一下(empirical speaking):

    a.完整特征工程竞赛
    b.野博客理论入门理解
    c.再回到代码深入理解模型内部
    d.再跨理论,查阅经典理论巨作。这时感性理性都有一定高度,会遇到很多很大的理解上的疑惑,这时3大经典可能就可以发挥到最大作用了。

    很多beginer,就比如说学CRF模型,然后一上来就摆一套复杂的公式,什么我就问,这能理解的了吗?这是正确的开启姿势吗?当然了,也要怪那些博主,直接整一大堆核心公式,实际上读者的理解门槛可能就是一个过渡性的细枝末节而已。没有上下文的教育肯定是失败的(这一点我又想吐槽国内绝大部分本科的院校教育模式)。所以说带有完整上下文信息以及过程来龙去脉交代清楚才算到位吧。

    而不是一上来就死啃被人推荐的“经典资料”,这一点相信部分同学会理解。好比以前本科零基础学c++ JAVA,上来就看primr TIJ,结果浪费了时间精力一直在门外兜圈。总结方法吸取教训,应该快速上手代码,才是最高效的。经典最好是用来查阅的工具书,我目前是李航周志华和经典的那3本迭代轮询看了好多轮,经常会反复查询某些model或理论的来龙去脉;有时候要查很多相关的东西,看这些书还是难以贯通,然后发现有些人的博客写的会更容易去理解。所以另外,学习资料渠道也要充分才行。

    最后提示一下,请务必按照标题层级结构和目录一级一级阅读,防止跟丢。

     

    二、Prerequisite

    2.1 概率图

    之前刚接触CRF时,一上来试图越过一堆繁琐的概率图相关概念,不过sad to say, 这是后面的前驱知识,后面还得反过来补这个点。所以若想整体把握,系统地拿下这一块,应该还是要越过这块门槛的。

    当然了,一开始只需略略快速看一篇,后面可再返过来补查。

    2.1.1 概览

    在统计概率图(probability graph models)中,参考宗成庆老师的书,是这样的体系结构(个人非常喜欢这种类型的图):

    在概率图模型中,数据(样本)由公式 G=(V,E) 建模表示:

    • V 表示节点,即随机变量(放在此处的,可以是一个token或者一个label),具体地,用 Y = (y_{1}, {\cdots}, y_{n} ) 为随机变量建模,注意 Y 现在是代表了一批随机变量(想象对应一条sequence,包含了很多的token), P(Y) 为这些随机变量的分布;
    • E 表示边,即概率依赖关系。具体咋理解,还是要在后面结合HMM或CRF的graph具体解释。

    2.1.2 有向图 vs. 无向图

    上图可以看到,贝叶斯网络(信念网络)都是有向的,马尔科夫网络无向。所以,贝叶斯网络适合为有单向依赖的数据建模,马尔科夫网络适合实体之间互相依赖的建模。具体地,他们的核心差异表现在如何求 P=(Y) ,即怎么表示 Y=(y_{1},\cdots,y_{n}) 这个的联合概率。

    1. 有向图

    对于有向图模型,这么求联合概率: P(x_{1}, {\cdots}, x_{n} )=\prod_{i=0}P(x_{i} | \pi(x_{i}))

    举个例子,对于下面的这个有向图的随机变量(注意,这个图我画的还是比较广义的):

    应该这样表示他们的联合概率:

    P(x_{1}, {\cdots}, x_{n} )=P(x_{1})·P(x_{2}|x_{1} )·P(x_{3}|x_{2} )·P(x_{4}|x_{2} )·P(x_{5}|x_{3},x_{4} )

    应该很好理解吧。

    2. 无向图

    对于无向图,我看资料一般就指马尔科夫网络(注意,这个图我画的也是比较广义的)。

    如果一个graph太大,可以用因子分解将 P=(Y) 写为若干个联合概率的乘积。咋分解呢,将一个图分为若干个“小团”,注意每个团必须是“最大团”(就是里面任何两个点连在了一块,具体……算了不解释,有点“最大连通子图”的感觉),则有:

     

    P(Y )=\frac{1}{Z(x)} \prod_{c}\psi_{c}(Y_{c} )

     

    , 其中 Z(x) = \sum_{Y} \prod_{c}\psi_{c}(Y_{c} ) ,公式应该不难理解吧,归一化是为了让结果算作概率。

    所以像上面的无向图:

    P(Y )=\frac{1}{Z(x)} ( \psi_{1}(X_{1}, X_{3}, X_{4} ) · \psi_{2}(X_{2}, X_{3}, X_{4} ) )

    其中, \psi_{c}(Y_{c} ) 是一个最大团 C 上随机变量们的联合概率,一般取指数函数的:

    \psi_{c}(Y_{c} ) = e^{-E(Y_{c})} =e^{\sum_{k}\lambda_{k}f_{k}(c,y|c,x)}

    好了,管这个东西叫做势函数。注意 e^{\sum_{k}\lambda_{k}f_{k}(c,y|c,x)} 是否有看到CRF的影子。

    那么概率无向图的联合概率分布可以在因子分解下表示为:

    P(Y )=\frac{1}{Z(x)} \prod_{c}\psi_{c}(Y_{c} ) = \frac{1}{Z(x)} \prod_{c} e^{\sum_{k}\lambda_{k}f_{k}(c,y|c,x)} = \frac{1}{Z(x)} e^{\sum_{c}\sum_{k}\lambda_{k}f_{k}(y_{i},y_{i-1},x,i)}

    注意,这里的理解还蛮重要的,注意递推过程,敲黑板,这是CRF的开端!
    这个由Hammersly-Clifford law保证,具体不展开。

    2.1.3 马尔科夫假设&马尔科夫性

    这个也属于前馈知识。

    1. 马尔科夫假设

    额应该是齐次马尔科夫假设,这样假设:马尔科夫链 (x_{1},\cdots,x_{n}) 里的 x_{i} 总是只受 x_{i-1} 一个人的影响。
    马尔科夫假设这里相当于就是个1-gram。

    马尔科夫过程呢?即,在一个过程中,每个状态的转移只依赖于前n个状态,并且只是个n阶的模型。最简单的马尔科夫过程是一阶的,即只依赖于器哪一个状态。

    2. 马尔科夫性

    马尔科夫性是是保证或者判断概率图是否为概率无向图的条件。

    三点内容:a. 成对,b. 局部,c. 全局。

    我觉得这个不用展开。

    2.2 判别式(discriminative)模型 vs. 生成式(generative)模型

    在监督学习下,模型可以分为判别式模型与生成式模型。

    重点来了。上面有提到,我理解了HMM、CRF模型的区别是从理解了判别式模型与生成式模型的那刻,并且瞬间对其他的模型有一个恍然大悟。我记得是一年前就开始纠结这两者的区别,但我只能说,栽在了一些烂博客上,大部分都没有自己的insightful理解,也就是一顿官话,也真是难以理解。后来在知乎上一直琢磨别人的答案,然后某日早晨终于豁然开朗,就是这种感觉。

    好了,我要用自己的理解来转述两者的区别了below。

    先问个问题,根据经验,A批模型(神经网络模型、SVM、perceptron、LR、DT……)与B批模型(NB、LDA……),有啥区别不?(这个问题需要一些模型使用经验)应该是这样的:

    1. A批模型是这么工作的,他们直接将数据的Y(或者label),根据所提供的features,学习,最后画出了一个明显或者比较明显的边界(具体怎么做到的?通过复杂的函数映射,或者决策叠加等等mechanism),这一点线性LR、线性SVM应该很明显吧。

    2. B批模型是这么工作的,他们先从训练样本数据中,将所有的数据的分布情况摸透,然后最终确定一个分布,来作为我的所有的输入数据的分布,并且他是一个联合分布 P(X,Y) (注意 X 包含所有的特征 x_{i}Y 包含所有的label)。然后我来了新的样本数据(inference),好,通过学习来的模型的联合分布 P(X,Y) ,再结合新样本给的 X ,通过条件概率就能出来 Y
    P(Y|X) = \frac{P(X,Y)}{P(X)}

    好了,应该说清楚了。

    1. 判别式模型

    那么A批模型对应了判别式模型。根据上面的两句话的区别,可以知道判别模型的特征了,所以有句话说:判别模型是直接对 P(Y|X)建模,就是说,直接根据X特征来对Y建模训练。

    具体地,我的训练过程是确定构件 P(Y|X) 模型里面“复杂映射关系”中的参数,完了再去inference一批新的sample。

    所以判别式模型的特征总结如下:

    1. P(Y|X) 建模
    2. 对所有的样本只构建一个模型,确认总体判别边界
    3. 观测到输入什么特征,就预测最可能的label
    4. 另外,判别式的优点是:对数据量要求没生成式的严格,速度也会快,小数据量下准确率也会好些。

    2. 生成式模型

    同样,B批模型对应了生成式模型。并且需要注意的是,在模型训练中,我学习到的是X与Y的联合模型 P(X,Y) ,也就是说,我在训练阶段是只对 P(X,Y)建模,我需要确定维护这个联合概率分布的所有的信息参数。完了之后在inference再对新的sample计算 P(Y|X) ,导出 Y ,但这已经不属于建模阶段了。

    结合NB过一遍生成式模型的工作流程。学习阶段,建模: P(X,Y)=P(X|Y)P(Y) (当然,NB具体流程去隔壁参考),然后 P(Y|X) = \frac{P(X,Y)}{P(X)}
    另外,LDA也是这样,只是他更过分,需要确定很多个概率分布,而且建模抽样都蛮复杂的。

    所以生成式总结下有如下特点:

    1. P(X,Y) 建模
    2. 这里我们主要讲分类问题,所以是要对每个label( y_{i} )都需要建模,最终选择最优概率的label为结果,所以没有什么判别边界。(对于序列标注问题,那只需要构件一个model)
    3. 中间生成联合分布,并可生成采样数据。
    4. 生成式模型的优点在于,所包含的信息非常齐全,我称之为“上帝信息”,所以不仅可以用来输入label,还可以干其他的事情。生成式模型关注结果是如何产生的。但是生成式模型需要非常充足的数据量以保证采样到了数据本来的面目,所以速度相比之下,慢。

    这一点明白后,后面讲到的HMM与CRF的区别也会非常清晰。
    最后identity the picture below:

    2.3 序列建模

    为了号召零门槛理解,现在解释如何为序列问题建模。

    序列包括时间序列以及general sequence,但两者无异。连续的序列在分析时也会先离散化处理。常见的序列有如:时序数据、本文句子、语音数据、等等。

    广义下的序列有这些特点:

    • 节点之间有关联依赖性/无关联依赖性
    • 序列的节点是随机的/确定的
    • 序列是线性变化/非线性的
    • ……

    对不同的序列有不同的问题需求,常见的序列建模方法总结有如下:

    1. 拟合,预测未来节点(或走势分析):

    a. 常规序列建模方法:AR、MA、ARMA、ARIMA

    b. 回归拟合

    c. Neural Networks

    2. 判断不同序列类别,即分类问题:HMM、CRF、General Classifier(ML models、NN models)

    3. 不同时序对应的状态的分析,即序列标注问题:HMM、CRF、RecurrentNNs

    在本篇文字中,我们只关注在2. & 3.类问题下的建模过程和方法。

     

    三、HMM

    最早接触的是HMM。较早做过一个项目,关于声波手势识别,跟声音识别的机制一样,使用的正是HMM的一套方法。后来又用到了kalman filter,之后做序列标注任务接触到了CRF,所以整个概率图模型还是接触的方面还蛮多。

    3.1 理解HMM

    在2.2、2.3中提序列的建模问题时,我们只是讨论了常规的序列数据,e.g., (X_{1},\cdots,X_{n}) ,像2.3的图片那样。像这种序列一般用马尔科夫模型就可以胜任。实际上我们碰到的更多的使用HMM的场景是每个节点 X_{i} 下还附带着另一个节点 Y_{i} ,正所谓隐含马尔科夫模型,那么除了正常的节点,还要将隐含状态节点也得建模进去。正儿八经地,将 X_{i} 、 Y_{i} 换成 i_{i} 、o_{i} ,并且他们的名称变为状态节点、观测节点。状态节点正是我的隐状态。

    HMM属于典型的生成式模型。对照2.1的讲解,应该是要从训练数据中学到数据的各种分布,那么有哪些分布呢以及是什么呢?直接正面回答的话,正是HMM的5要素,其中有3个就是整个数据的不同角度的概率分布:

    • N ,隐藏状态集 N = \lbrace q_{1}, \cdots, q_{N} \rbrace , 我的隐藏节点不能随意取,只能限定取包含在隐藏状态集中的符号。
    • M ,观测集 M = \lbrace v_{1}, \cdots, v_{M} \rbrace , 同样我的观测节点不能随意取,只能限定取包含在观测状态集中的符号。
    • A ,状态转移概率矩阵,这个就是其中一个概率分布。他是个矩阵, A= [a_{ij}]_{N \times N} (N为隐藏状态集元素个数),其中 a_{ij} = P(i_{t+1}|i_{t}), i_{t} 即第i个隐状态节点,即所谓的状态转移嘛。
    • B ,观测概率矩阵,这个就是另一个概率分布。他是个矩阵, B = [b_{ij}]_{N \times M} (N为隐藏状态集元素个数,M为观测集元素个数),其中 b_{ij} = P(o_{t}|i_{t}), o_{t} 即第i个观测节点, i_{t} 即第i个隐状态节点,即所谓的观测概率(发射概率)嘛。
    • π ,在第一个隐状态节点 i_{t} ,我得人工单独赋予,我第一个隐状态节点的隐状态是 N 中的每一个的概率分别是多少,然后 π 就是其概率分布。

    所以图看起来是这样的:

    看的很清楚,我的模型先去学习要确定以上5要素,之后在inference阶段的工作流程是:首先,隐状态节点 i_{t} 是不能直接观测到的数据节点, o_{t} 才是能观测到的节点,并且注意箭头的指向表示了依赖生成条件关系, i_{t} 在A的指导下生成下一个隐状态节点 i_{t+1} ,并且 i_{t}B 的指导下生成依赖于该 i_{t} 的观测节点 o_{t} , 并且我只能观测到序列 (o_{1}, \cdots, o_{i})

    好,举例子说明(序列标注问题,POS,标注集BES):

    input: "学习出一个模型,然后再预测出一条指定"

    expected output: 学/B 习/E 出/S 一/B 个/E 模/B 型/E ,/S 然/B 后/E 再/E 预/B 测/E ……

    其中,input里面所有的char构成的字表,形成观测集 M ,因为字序列在inference阶段是我所能看见的;标注集BES构成隐藏状态集 N ,这是我无法直接获取的,也是我的预测任务;至于 A、B、π ,这些概率分布信息(上帝信息)都是我在学习过程中所确定的参数。

    然后一般初次接触的话会疑问:为什么要这样?……好吧,就应该是这样啊,根据具有同时带着隐藏状态节点和观测节点的类型的序列,在HMM下就是这样子建模的。

    下面来点高层次的理解:

    1. 根据概率图分类,可以看到HMM属于有向图,并且是生成式模型,直接对联合概率分布建模 P(O,I) = \sum_{t=1}^{T}P(O_{t} | O_{t-1})P(I_{t} | O_{t}) (注意,这个公式不在模型运行的任何阶段能体现出来,只是我们都去这么来表示HMM是个生成式模型,他的联合概率 P(O,I) 就是这么计算的)。
    2. 并且B中 b_{ij} = P(o_{t}|i_{t}) ,这意味着o对i有依赖性。
    3. 在A中, a_{ij} = P(i_{t+1}|i_{t}) ,也就是说只遵循了一阶马尔科夫假设,1-gram。试想,如果数据的依赖超过1-gram,那肯定HMM肯定是考虑不进去的。这一点限制了HMM的性能。

    3.2 模型运行过程

    模型的运行过程(工作流程)对应了HMM的3个问题。

    3.2.1 学习训练过程

    对照2.1的讲解,HMM学习训练的过程,就是找出数据的分布情况,也就是模型参数的确定。

    主要学习算法按照训练数据除了观测状态序列 (o_{1}, \cdots, o_{i}) 是否还有隐状态序列 (i_{1}, \cdots, i_{i}) 分为:

    • 极大似然估计, with 隐状态序列
    • Baum-Welch(前向后向), without 隐状态序列

    感觉不用做很多的介绍,都是很实实在在的算法,看懂了就能理解。简要提一下。

    1. 极大似然估计

    一般做NLP的序列标注等任务,在训练阶段肯定是有隐状态序列的。所以极大似然估计法是非常常用的学习算法,我见过的很多代码里面也是这么计算的。比较简单。

    • step1. 算A

    \hat{a_{ij}} = \frac{A_{ij}}{\sum_{j=1}^{N}A_{ij}}

    • step2. 算B

    \hat{b_{j}}(k) = \frac{B_{jk}}{\sum_{k=1}^{M}B_{jk}}

    • step3. 直接估计 π

    比如说,在代码里计算完了就是这样的:

     

    2. Baum-Welch(前向后向)

    就是一个EM的过程,如果你对EM的工作流程有经验的话,对这个Baum-Welch一看就懂。EM的过程就是初始化一套值,然后迭代计算,根据结果再调整值,再迭代,最后收敛……好吧,这个理解是没有捷径的,去隔壁钻研EM吧。

    这里只提一下核心。因为我们手里没有隐状态序列 (i_{1}, \cdots, i_{i}) 信息,所以我先必须给初值 a_{ij}^{0}, b_{j}(k)^{0}, \pi^{0} ,初步确定模型,然后再迭代计算出 a_{ij}^{n}, b_{j}(k)^{n}, \pi^{n} ,中间计算过程会用到给出的观测状态序列 (o_{1}, \cdots, o_{i}) 。另外,收敛性由EM的XXX定理保证。

    3.2.2 序列标注(解码)过程

    好了,学习完了HMM的分布参数,也就确定了一个HMM模型。需要注意的是,这个HMM是对我这一批全部的数据进行训练所得到的参数。

    序列标注问题也就是“预测过程”,通常称为解码过程。对应了序列建模问题3.。对于序列标注问题,我们只需要学习出一个HMM模型即可,后面所有的新的sample我都用这一个HMM去apply。

    我们的目的是,在学习后已知了 P(Q,O) ,现在要求出 P(Q|O) ,进一步

    Q_{max} = argmax_{allQ}\frac{P(Q,O)}{P(O)}

    再直白点就是,我现在要在给定的观测序列下找出一条隐状态序列,条件是这个隐状态序列的概率是最大的那个。

     

    具体地,都是用Viterbi算法解码,是用DP思想减少重复的计算。Viterbi也是满大街的,不过要说的是,Viterbi不是HMM的专属,也不是任何模型的专属,他只是恰好被满足了被HMM用来使用的条件。谁知,现在大家都把Viterbi跟HMM捆绑在一起了, shame。

    Viterbi计算有向无环图的一条最大路径,应该还好理解。如图:

    关键是注意,每次工作热点区只涉及到t 与 t-1,这对应了DP的无后效性的条件。如果对某些同学还是很难理解,请参考这个答案下@Kiwee的回答吧。

    3.2.3 序列概率过程

    我通过HMM计算出序列的概率又有什么用?针对这个点我把这个问题详细说一下。

    实际上,序列概率过程对应了序列建模问题2.,即序列分类。
    在3.2.2第一句话我说,在序列标注问题中,我用一批完整的数据训练出了一支HMM模型即可。好,那在序列分类问题就不是训练一个HMM模型了。我应该这么做(结合语音分类识别例子):

    目标:识别声音是A发出的还是B发出的。
    HMM建模过程:
    1. 训练:我将所有A说的语音数据作为dataset_A,将所有B说的语音数据作为dataset_B(当然,先要分别对dataset A ,B做预处理encode为元数据节点,形成sequences),然后分别用dataset_A、dataset_B去训练出HMM_A/HMM_B
    2. inference:来了一条新的sample(sequence),我不知道是A的还是B的,没问题,分别用HMM_A/HMM_B计算一遍序列的概率得到 P_{A}(S)、P_{B}(S) ,比较两者大小,哪个概率大说明哪个更合理,更大概率作为目标类别。

     

    所以,本小节的理解重点在于,如何对一条序列计算其整体的概率。即目标是计算出 P(O|λ) 。这个问题前辈们在他们的经典中说的非常好了,比如参考李航老师整理的:

    • 直接计算法(穷举搜索)
    • 前向算法
    • 后向算法

    后面两个算法采用了DP思想,减少计算量,即每一次直接引用前一个时刻的计算结果以避免重复计算,跟Viterbi一样的技巧。

    还是那句,因为这篇文档不是专门讲算法细节的,所以不详细展开这些。毕竟,所有的科普HMM、CRF的博客貌似都是在扯这些算法,妥妥的街货,就不搬运了。

     

    四、MEMM

    MEMM,即最大熵马尔科夫模型,这个是在接触了HMM、CRF之后才知道的一个模型。说到MEMM这一节时,得转换思维了,因为现在这MEMM属于判别式模型。

    不过有一点很尴尬,MEMM貌似被使用或者讲解引用的不及HMM、CRF。

    4.1 理解MEMM

    这里还是啰嗦强调一下,MEMM正因为是判别模型,所以不废话,我上来就直接为了确定边界而去建模,比如说序列求概率(分类)问题,我直接考虑找出函数分类边界。这一点跟HMM的思维方式发生了很大的变化,如果不对这一点有意识,那么很难理解为什么MEMM、CRF要这么做。

    HMM中,观测节点 o_{i} 依赖隐藏状态节点 i_{i} ,也就意味着我的观测节点只依赖当前时刻的隐藏状态。但在更多的实际场景下,观测序列是需要很多的特征来刻画的,比如说,我在做NER时,我的标注 i_{i} 不仅跟当前状态 o_{i} 相关,而且还跟前后标注 o_{j}(j \neq i) 相关,比如字母大小写、词性等等。

    为此,提出来的MEMM模型就是能够直接允许“定义特征”,直接学习条件概率,即 P(i_{i}|i_{i-1},o_{i}) (i = 1,\cdots,n) , 总体为:

    P(I|O) = \prod_{t=1}^{n}P(i_{i}|i_{i-1},o_{i}), i = 1,\cdots,n

    并且, P(i|i^{'},o) 这个概率通过最大熵分类器建模(取名MEMM的原因):

    P(i|i^{'},o) = \frac{1}{Z(o,i^{'})} exp(\sum_{a})\lambda_{a}f_{a}(o,i)

    重点来了,这是ME的内容,也是理解MEMM的关键: Z(o,i^{'}) 这部分是归一化; f_{a}(o,i)特征函数,具体点,这个函数是需要去定义的; λ 是特征函数的权重,这是个未知参数,需要从训练阶段学习而得。

    比如我可以这么定义特征函数:

    \begin{equation} f_{a}(o,i) = \begin{cases} 1& \text{满足特定条件},\\ 0& \text{other} \end{cases} \end{equation}

    其中,特征函数 f_{a}(o,i) 个数可任意制定, (a = 1, \cdots, n)

     

    所以总体上,MEMM的建模公式这样:

    P(I|O) = \prod_{t=1}^{n}\frac{ exp(\sum_{a})\lambda_{a}f_{a}(o,i) }{Z(o,i_{i-1})} , i = 1,\cdots,n

     

    是的,公式这部分之所以长成这样,是由ME模型决定的。

    请务必注意,理解判别模型定义特征两部分含义,这已经涉及到CRF的雏形了。

    所以说,他是判别式模型,直接对条件概率建模。 上图:

    MEMM需要两点注意:

    1. 与HMM的 o_{i} 依赖 i_{i} 不一样,MEMM当前隐藏状态 i_{i} 应该是依赖当前时刻的观测节点 o_{i} 和上一时刻的隐藏节点 i_{i-1}
    2. 需要注意,之所以图的箭头这么画,是由MEMM的公式决定的,而公式是creator定义出来的。

    好了,走一遍完整流程。

    step1. 先预定义特征函数 f_{a}(o,i)
    step2. 在给定的数据上,训练模型,确定参数,即确定了MEMM模型
    step3. 用确定的模型做序列标注问题或者序列求概率问题。

    4.2 模型运行过程

    MEMM模型的工作流程也包括了学习训练问题、序列标注问题、序列求概率问题。

    4.2.1 学习训练过程

    一套MEMM由一套参数唯一确定,同样地,我需要通过训练数据学习这些参数。MEMM模型很自然需要学习里面的特征权重λ。

    不过跟HMM不用的是,因为HMM是生成式模型,参数即为各种概率分布元参数,数据量足够可以用最大似然估计。而判别式模型是用函数直接判别,学习边界,MEMM即通过特征函数来界定。但同样,MEMM也有极大似然估计方法、梯度下降、牛顿迭代发、拟牛顿下降、BFGS、L-BFGS等等。各位应该对各种优化方法有所了解的。

    嗯,具体详细求解过程貌似问题不大。

    4.2.2 序列标注过程

    还是跟HMM一样的,用学习好的MEMM模型,在新的sample(观测序列 o_{1}, \cdots, o_{i} )上找出一条概率最大最可能的隐状态序列 i_{1}, \cdots, i_{i}

    只是现在的图中的每个隐状态节点的概率求法有一些差异而已,正确将每个节点的概率表示清楚,路径求解过程还是一样,采用viterbi算法。

    4.2.3 序列求概率过程

    跟HMM举的例子一样的,也是分别去为每一批数据训练构建特定的MEMM,然后根据序列在每个MEMM模型的不同得分概率,选择最高分数的模型为wanted类别。

    应该可以不用展开,吧……

    4.3 标注偏置?

    MEMM讨论的最多的是他的labeling bias 问题。

    1. 现象

    是从街货上烤过来的……

    用Viterbi算法解码MEMM,状态1倾向于转换到状态2,同时状态2倾向于保留在状态2。 解码过程细节(需要会viterbi算法这个前提):

    P(1-> 1-> 1-> 1)= 0.4 x 0.45 x 0.5 = 0.09 ,
    P(2->2->2->2)= 0.2 X 0.3 X 0.3 = 0.018,
    P(1->2->1->2)= 0.6 X 0.2 X 0.5 = 0.06,
    P(1->1->2->2)= 0.4 X 0.55 X 0.3 = 0.066

    但是得到的最优的状态转换路径是1->1->1->1,为什么呢?因为状态2可以转换的状态比状态1要多,从而使转移概率降低,即MEMM倾向于选择拥有更少转移的状态。

    2. 解释原因

    直接看MEMM公式:

    P(I|O) = \prod_{t=1}^{n}\frac{ exp(\sum_{a})\lambda_{a}f_{a}(o,i) }{Z(o,i_{i-1})} , i = 1,\cdots,n

    ∑ 求和的作用在概率中是归一化,但是这里归一化放在了指数内部,管这叫local归一化。 来了,viterbi求解过程,是用dp的状态转移公式(MEMM的没展开,请参考CRF下面的公式),因为是局部归一化,所以MEMM的viterbi的转移公式的第二部分出现了问题,导致dp无法正确的递归到全局的最优。

    \delta_{i+1} = max_{1 \le j \le m}\lbrace \delta_{i}(I) + \sum_{i}^{T}\sum_{k}^{M}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i) \rbrace

     

    五、CRF

    我觉得一旦有了一个清晰的工作流程,那么按部就班地,没有什么很难理解的地方,因为整体框架已经胸有成竹了,剩下了也只有添砖加瓦小修小补了。有了上面的过程基础,CRF也是类似的,只是有方法论上的细微区别。

    5.1 理解CRF

    请看第一张概率图模型构架图,CRF上面是马尔科夫随机场(马尔科夫网络),而条件随机场是在给定的随机变量 X (具体,对应观测序列 o_{1}, \cdots, o_{i} )条件下,随机变量 Y (具体,对应隐状态序列 i_{1}, \cdots, i_{i} 的马尔科夫随机场。
    广义的CRF的定义是: 满足 P(Y_{v}|X,Y_{w},w \neq v) = P(Y_{v}|X,Y_{w},w \sim v) 的马尔科夫随机场叫做条件随机场(CRF)。

    不过一般说CRF为序列建模,就专指CRF线性链(linear chain CRF):

    在2.1.2中有提到过,概率无向图的联合概率分布可以在因子分解下表示为:

    P(Y | X)=\frac{1}{Z(x)} \prod_{c}\psi_{c}(Y_{c}|X ) = \frac{1}{Z(x)} \prod_{c} e^{\sum_{k}\lambda_{k}f_{k}(c,y|c,x)} = \frac{1}{Z(x)} e^{\sum_{c}\sum_{k}\lambda_{k}f_{k}(y_{i},y_{i-1},x,i)}

    而在线性链CRF示意图中,每一个( I_{i} \sim O_{i} )对为一个最大团,即在上式中 c = i 。并且线性链CRF满足 P(I_{i}|O,I_{1},\cdots, I_{n}) = P(I_{i}|O,I_{i-1},I_{i+1})

    所以CRF的建模公式如下:

    P(I | O)=\frac{1}{Z(O)} \prod_{i}\psi_{i}(I_{i}|O ) = \frac{1}{Z(O)} \prod_{i} e^{\sum_{k}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i)} = \frac{1}{Z(O)} e^{\sum_{i}\sum_{k}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i)}

    我要敲黑板了,这个公式是非常非常关键的,注意递推过程啊,我是怎么从 ∏ 跳到 e^{\sum} 的。

    不过还是要多啰嗦一句,想要理解CRF,必须判别式模型的概念要深入你心。正因为是判别模型,所以不废话,我上来就直接为了确定边界而去建模,因为我创造出来就是为了这个分边界的目的的。比如说序列求概率(分类)问题,我直接考虑找出函数分类边界。所以才为什么会有这个公式。所以再看到这个公式也别懵逼了,he was born for discriminating the given data from different classes. 就这样。不过待会还会具体介绍特征函数部分的东西。

     

    除了建模总公式,关键的CRF重点概念在MEMM中已强调过:判别式模型特征函数

    1. 特征函数

    上面给出了CRF的建模公式:

    P(I | O)=\frac{1}{Z(O)} e^{\sum_{i}^{T}\sum_{k}^{M}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i)}

    • 下标i表示我当前所在的节点(token)位置。
    • 下标k表示我这是第几个特征函数,并且每个特征函数都附属一个权重 \lambda_{k} ,也就是这么回事,每个团里面,我将为 token_{i} 构造M个特征,每个特征执行一定的限定作用,然后建模时我再为每个特征函数加权求和。
    • Z(O) 是用来归一化的,为什么?想想LR以及softmax为何有归一化呢,一样的嘛,形成概率值。
    • 再来个重要的理解。 P(I|O) 这个表示什么?具体地,表示了在给定的一条观测序列 O=(o_{1},\cdots, o_{i}) 条件下,我用CRF所求出来的隐状态序列 I=(i_{1},\cdots, i_{i}) 的概率,注意,这里的I是一条序列,有多个元素(一组随机变量),而至于观测序列 O=(o_{1},\cdots, o_{i}) ,它可以是一整个训练语料的所有的观测序列;也可以是在inference阶段的一句sample,比如说对于序列标注问题,我对一条sample进行预测,可能能得到 P_{j}(I | O)(j=1,…,J)J条隐状态I,但我肯定最终选的是最优概率的那条(by viterbi)。这一点希望你能理解。

    对于CRF,可以为他定义两款特征函数:转移特征&状态特征。 我们将建模总公式展开:

    P(I | O)=\frac{1}{Z(O)} e^{\sum_{i}^{T}\sum_{k}^{M}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i)}=\frac{1}{Z(O)} e^{ [ \sum_{i}^{T}\sum_{j}^{J}\lambda_{j}t_{j}(O,I_{i-1},I_{i},i) + \sum_{i}^{T}\sum_{l}^{L}\mu_{l}s_{l}(O,I_{i},i) ] }

     

    其中:

    • t_{j} 为i处的转移特征,对应权重 \lambda_{j} ,每个 token_{i} 都有J个特征,转移特征针对的是前后token之间的限定。
      • 举个例子:

    \begin{equation} t_{k=1}(o,i) = \begin{cases} 1& \text{满足特定转移条件,比如前一个token是‘I’},\\ 0& \text{other} \end{cases} \end{equation}

    • sl为i处的状态特征,对应权重μl,每个tokeni都有L个特征
      • 举个例子:

    \begin{equation} s_{l=1}(o,i) = \begin{cases} 1& \text{满足特定状态条件,比如当前token的POS是‘V’},\\ 0& \text{other} \end{cases} \end{equation}

     

    不过一般情况下,我们不把两种特征区别的那么开,合在一起:

    P(I | O)=\frac{1}{Z(O)} e^{\sum_{i}^{T}\sum_{k}^{M}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i)}

     

    满足特征条件就取值为1,否则没贡献,甚至你还可以让他打负分,充分惩罚。

    再进一步理解的话,我们需要把特征函数部分抠出来:

    Score = \sum_{i}^{T}\sum_{k}^{M}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i)

     

    是的,我们为 token_{i} 打分,满足条件的就有所贡献。最后将所得的分数进行log线性表示,求和后归一化,即可得到概率值……完了又扯到了log线性模型。现在稍作解释:

    log-linear models take the following form:
    P(y|x;\omega) = \frac{ exp(\omega·\phi(x,y)) }{ \sum_{y^{'}\in Y }exp(\omega·\phi(x,y^{‘})) }

    我觉得对LR或者sotfmax熟悉的对这个应该秒懂。然后CRF完美地满足这个形式,所以又可以归入到了log-linear models之中。

    5.2 模型运行过程

    模型的工作流程,跟MEMM是一样的:

    • step1. 先预定义特征函数 f_{a}(o,i)
    • step2. 在给定的数据上,训练模型,确定参数 \lambda_{k}
    • step3. 用确定的模型做序列标注问题或者序列求概率问题

    可能还是没做到100%懂,结合例子说明:

    ……

    5.2.1 学习训练过程

    一套CRF由一套参数λ唯一确定(先定义好各种特征函数)。

    同样,CRF用极大似然估计方法、梯度下降、牛顿迭代、拟牛顿下降、IIS、BFGS、L-BFGS等等。各位应该对各种优化方法有所了解的。其实能用在log-linear models上的求参方法都可以用过来。

    嗯,具体详细求解过程貌似问题不大。

    5.2.2 序列标注过程

    还是跟HMM一样的,用学习好的CRF模型,在新的sample(观测序列 o_{1}, \cdots, o_{i} )上找出一条概率最大最可能的隐状态序列 i_{1}, \cdots, i_{i}

    只是现在的图中的每个隐状态节点的概率求法有一些差异而已,正确将每个节点的概率表示清楚,路径求解过程还是一样,采用viterbi算法。

    啰嗦一下,我们就定义i处的局部状态为 \delta_{i}(I) ,表示在位置i处的隐状态的各种取值可能为I,然后递推位置i+1处的隐状态,写出来的DP转移公式为:

    \delta_{i+1} = max_{1 \le j \le m}\lbrace \delta_{i}(I) + \sum_{i}^{T}\sum_{k}^{M}\lambda_{k}f_{k}(O,I_{i-1},I_{i},i) \rbrace

    这里没写规范因子 Z(O) 是因为不规范化不会影响取最大值后的比较。

    具体还是不展开为好。

    5.2.3 序列求概率过程

    跟HMM举的例子一样的,也是分别去为每一批数据训练构建特定的CRF,然后根据序列在每个MEMM模型的不同得分概率,选择最高分数的模型为wanted类别。只是貌似很少看到拿CRF或者MEMM来做分类的,直接用网络模型不就完了不……

    应该可以不用展开,吧……

    5.3 CRF++分析

    本来做task用CRF++跑过baseline,后来在对CRF做调研时,非常想透析CRF++的工作原理,以identify以及verify做的各种假设猜想。当然,也看过其他的CRF实现源码。

    所以干脆写到这里来,结合CRF++实例讲解过程。

    有一批语料数据,并且已经tokenized好了:

    Nuclear
    theory
    devoted
    major
    efforts
    ……

    并且我先确定了13个标注元素:

    B_MAT
    B_PRO
    B_TAS
    E_MAT
    E_PRO
    E_TAS
    I_MAT
    I_PRO
    I_TAS
    O
    S_MAT
    S_PRO
    S_TAS

    1. 定义模板

    按道理应该是定义特征函数才对吧?好的,在CRF++下,应该是先定义特征模板,然后用模板自动批量产生大量的特征函数。我之前也蛮confused的,用完CRF++还以为模板就是特征,后面就搞清楚了:每一条模板将在每一个token处生产若干个特征函数。

    CRF++的模板(template)有U系列(unigram)、B系列(bigram),不过我至今搞不清楚B系列的作用,因为U模板都可以完成2-gram的作用。

    U00:%x[-2,0]
    U01:%x[-1,0]
    U02:%x[0,0]
    U03:%x[1,0]
    U04:%x[2,0]

    U05:%x[-2,0]/%x[-1,0]/%x[0,0]
    U06:%x[-1,0]/%x[0,0]/%x[1,0]
    U07:%x[0,0]/%x[1,0]/%x[2,0]
    U08:%x[-1,0]/%x[0,0]
    U09:%x[0,0]/%x[1,0]

    B

    所以,U00 - U09 我定义了10个模板。

    2. 产生特征函数

    是的,会产生大量的特征。 U00 - U04的模板产生的是状态特征函数;U05 - U09的模板产生的是转移特征函数。

    在CRF++中,每个特征都会try每个标注label(这里有13个),总共将生成 N * L = i * k^{'} * L 个特征函数以及对应的权重出来。N表示每一套特征函数 N= i * k^{'} ,L表示标注集元素个数。

    比如训练好的CRF模型的部分特征函数是这样存储的:

    22607 B
    790309 U00:%
    3453892 U00:%)
    2717325 U00:&
    2128269 U00:'t
    2826239 U00:(0.3534
    2525055 U00:(0.593–1.118
    197093 U00:(1)
    2079519 U00:(1)L=14w2−12w−FμνaFaμν
    2458547 U00:(1)δn=∫−∞En+1ρ˜(E)dE−n
    1766024 U00:(1.0g
    2679261 U00:(1.1wt%)
    1622517 U00:(100)
    727701 U00:(1000–5000A)
    2626520 U00:(10a)
    2626689 U00:(10b)
    ……
    2842814 U07:layer/thicknesses/Using
    2847533 U07:layer/thicknesses/are
    2848651 U07:layer/thicknesses/in
    331539 U07:layer/to/the
    1885871 U07:layer/was/deposited
    ……(数量非常庞大)

    其实也就是对应了这样些个特征函数:

    func1 = if (output = B and feature="U02:一") return 1 else return 0
    func2 = if (output = M and feature="U02:一") return 1 else return 0
    func3 = if (output = E and feature="U02:一") return 1 else return 0
    func4 = if (output = S and feature="U02:一") return 1 else return 0

    比如模板U06会从语料中one by one逐句抽出这些各个特征:

    一/个/人/……
    个/人/走/……

    3. 求参

    对上述的各个特征以及初始权重进行迭代参数学习。

    在CRF++ 训练好的模型里,权重是这样的:

    0.3972716048310705
    0.5078838237171732
    0.6715316559507898
    -0.4198827647512405
    -0.4233310655891150
    -0.4176580083832543
    -0.4860489836004728
    -0.6156475863742051
    -0.6997919485753300
    0.8309956709647820
    0.3749695682658566
    0.2627347894057647
    0.0169732441379157
    0.3972716048310705
    0.5078838237171732
    0.6715316559507898
    ……(数量非常庞大,与每个label的特征函数对应,我这有300W个)

    4. 预测解码

    结果是这样的:

    Nuclear BTAS
    theory E
    TAS
    devoted O
    major O
    efforts O
    ……

    5.4 LSTM+CRF

    LSTM+CRF这个组合其实我在知乎上答过问题,然后顺便可以整合到这里来。

    1、perspectively

    大家都知道,LSTM已经可以胜任序列标注问题了,为每个token预测一个label(LSTM后面接:分类器);而CRF也是一样的,为每个token预测一个label。

    但是,他们的预测机理是不同的。CRF是全局范围内统计归一化的条件状态转移概率矩阵,再预测出一条指定的sample的每个token的label;LSTM(RNNs,不区分here)是依靠神经网络的超强非线性拟合能力,在训练时将samples通过复杂到让你窒息的高阶高纬度异度空间的非线性变换,学习出一个模型,然后再预测出一条指定的sample的每个token的label。

    2、LSTM+CRF

    既然LSTM都OK了,为啥researchers搞一个LSTM+CRF的hybrid model?

    哈哈,因为a single LSTM预测出来的标注有问题啊!举个segmentation例子(BES; char level),plain LSTM 会搞出这样的结果:

    input: "学习出一个模型,然后再预测出一条指定"
    expected output: 学/B 习/E 出/S 一/B 个/E 模/B 型/E ,/S 然/B 后/E 再/E 预/B 测/E ……
    real output: 学/B 习/E 出/S 一/B 个/B 模/B 型/E ,/S 然/B 后/B 再/E 预/B 测/E ……

    看到不,用LSTM,整体的预测accuracy是不错indeed, 但是会出现上述的错误:在B之后再来一个B。这个错误在CRF中是不存在的,因为CRF的特征函数的存在就是为了对given序列观察学习各种特征(n-gram,窗口),这些特征就是在限定窗口size下的各种词之间的关系。然后一般都会学到这样的一条规律(特征):B后面接E,不会出现E。这个限定特征会使得CRF的预测结果不出现上述例子的错误。当然了,CRF还能学到更多的限定特征,那越多越好啊!

    好了,那就把CRF接到LSTM上面,把LSTM在timestep上把每一个hiddenstate的tensor输入给CRF,让LSTM负责在CRF的特征限定下,依照新的loss function,学习出一套新的非线性变换空间。

    最后,不用说,结果还真是好多了呢。

    LSTM+CRF codes, here. Go just take it.

     

    六、总结

    1. 总体对比

    应该看到了熟悉的图了,现在看这个图的话,应该可以很清楚地get到他所表达的含义了。这张图的内容正是按照生成式&判别式来区分的,NB在sequence建模下拓展到了HMM;LR在sequence建模下拓展到了CRF。

    2. HMM vs. MEMM vs. CRF

    将三者放在一块做一个总结:

    1. HMM -> MEMM: HMM模型中存在两个假设:一是输出观察值之间严格独立,二是状态的转移过程中当前状态只与前一状态有关。但实际上序列标注问题不仅和单个词相关,而且和观察序列的长度,单词的上下文,等等相关。MEMM解决了HMM输出独立性假设的问题。因为HMM只限定在了观测与状态之间的依赖,而MEMM引入自定义特征函数,不仅可以表达观测之间的依赖,还可表示当前观测与前后多个状态之间的复杂依赖。
    2. MEMM -> CRF:
    • CRF不仅解决了HMM输出独立性假设的问题,还解决了MEMM的标注偏置问题,MEMM容易陷入局部最优是因为只在局部做归一化,而CRF统计了全局概率,在做归一化时考虑了数据在全局的分布,而不是仅仅在局部归一化,这样就解决了MEMM中的标记偏置的问题。使得序列标注的解码变得最优解。
    • HMM、MEMM属于有向图,所以考虑了x与y的影响,但没讲x当做整体考虑进去(这点问题应该只有HMM)。CRF属于无向图,没有这种依赖性,克服此问题。

     

    3. Machine Learning models vs. Sequential models

    为了一次将概率图模型理解的深刻到位,我们需要再串一串,更深度与原有的知识体系融合起来。

    机器学习模型,按照学习的范式或方法,以及加上自己的理解,给常见的部分的他们整理分了分类(主流上,都喜欢从训练样本的歧义型分,当然也可以从其他角度来):

    一、监督:{
    
    1.1 分类算法(线性和非线性):{
    
        感知机
    
        KNN
    
        概率{
            朴素贝叶斯(NB)
            Logistic Regression(LR)
            最大熵MEM(与LR同属于对数线性分类模型)
        }
    
        支持向量机(SVM)
    
        决策树(ID3、CART、C4.5)
    
        assembly learning{
            Boosting{
                Gradient Boosting{
                    GBDT
                    xgboost(传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题);xgboost是Gradient Boosting的一种高效系统实现,并不是一种单一算法。)
                }
                AdaBoost
            }   
            Bagging{
                随机森林
            }
            Stacking
        }
    
        ……
    }
    
    1.2 概率图模型:{
        HMM
        MEMM(最大熵马尔科夫)
        CRF
        ……
    }
    
    
    1.3 回归预测:{
        线性回归
        树回归
        Ridge岭回归
        Lasso回归
        ……
    }
    
    ……  
    }
    
    
    二、非监督:{
    2.1 聚类:{
        1. 基础聚类
            K—mean
            二分k-mean
            K中值聚类
            GMM聚类
        2. 层次聚类
        3. 密度聚类
        4. 谱聚类()
    }
    
    2.2 主题模型:{
        pLSA
        LDA隐含狄利克雷分析
    }
    
    2.3 关联分析:{
        Apriori算法
        FP-growth算法
    }
    
    2.4 降维:{
        PCA算法
        SVD算法
        LDA线性判别分析
        LLE局部线性嵌入
    }
    
    2.5 异常检测:
    ……
    }
    
    三、半监督学习
    
    四、迁移学习
    

     

    (注意到,没有把神经网络体系加进来。因为NNs的范式很灵活,不太适用这套分法,largely, off this framework)

    Generally speaking,机器学习模型,尤其是有监督学习,一般是为一条sample预测出一个label,作为预测结果。 但与典型常见的机器学习模型不太一样,序列模型(概率图模型)是试图为一条sample里面的每个基本元数据分别预测出一个label。这一点,往往是beginner伊始难以理解的。

    具体的实现手段差异,就是:ML models通过直接预测得出label;Sequential models是给每个token预测得出label还没完,还得将他们每个token对应的labels进行组合,具体的话,用viterbi来挑选最好的那个组合。

     

    over

     

     

    有了这道开胃菜,接下来,读者可以完成这些事情:完善细节算法、阅读原著相关论文达到彻底理解、理解相关拓展概念、理论创新……

     

    hope those hlpe!

    欢迎留言!

    有错误之处请多多指正,谢谢!

     

    Referrences:

    《统计学习方法》,李航

    《统计自然语言处理》,宗成庆

    《 An Introduction to Conditional Random Fields for Relational Learning》, Charles Sutton, Andrew McCallum

    《Log-Linear Models, MEMMs, and CRFs》,ichael Collins

     

    如何用简单易懂的例子解释条件随机场(CRF)模型?它和HMM有什么区别?

    【中文分词】最大熵马尔可夫模型MEMM - Treant - 博客园

    【中文分词】最大熵马尔可夫模型MEMM - Treant - 博客园

    timvieira/crf

    shawntan/python-crf

    Log-linear Models and Conditional Random Fields

    如何轻松愉快地理解条件随机场(CRF)?

    条件随机场CRF(三) 模型学习与维特比算法解码

    crf++里的特征模板得怎么理解?

    CRF++代码分析-码农场

    CRF++源码解读 - CSDN博客

    CRF++模型格式说明-码农场

    标注偏置问题(Label Bias Problem)和HMM、MEMM、CRF模型比较<转>

    展开全文
  • 目前系统中新增了分销渠道产品组,配置后,对应销售范围创建的订单收入,也能够归集到对应的会计科目中 FS01N可以验证这一点 但是 使用KE30获利能力分析的时候,看不到新增的分销渠道,也就看不到对应的收入 而且...

    我遇到的问题:

    系统中新增了新的分销渠道和产品组。销售范围(sales area)对应的订单类型被创建(va01)并过账后,也能够归集到对应的会计科目中(FS01N可以验证这一点)。

    但是

    使用KE30获利能力分析的时候,看不到新增的分销渠道,也就看不到对应的收入。(这个描述不准确)

    而且这个情况影响到CE11000这个表,也就是这个表中没有(本应该有)累计新增销售范围带来的收入

    我就想知道,CE11000到底是什么表?谁创建的?数据来源是什么?为什么新增销售范围带来的收入无法被归集入CE11000

    *--------------------------------------------------------------------------------------------------------------------

    下面是我找到的答案:

    我遇到的问题,官方名称是,管理会计的获利能力分析问题。

    SAP的获利能力分析貌似简称COPA 正确的说是CO-PA 分开来写,就是管理会计(CO)的获利能力分析(PA)

    CO可能就是管理会计(控制会计)模块的简称

    PA 是 Profitability Analysis


    下面有个英文CO-PA的文档,免费下载,其实不建议看,我就是列在这里。

    百度文库英文版COPA获利能力分析book

    *-----------------------------------------------------------------------------------------------------------------------

    带着上述疑问,我请教了SAP群中的朋友,一网友指出是 创建经营范围的时候 由系统创建的表格

    SAP经营范围相关表

    SAP获利能力报表常用事务码

    SAP常用业务数据表关系

    关于维护经营范围,也总结几点:

    1.在建立data structure,SAP做了什么动作?

    在建立OC->STOC,系统会产生这样一个 结构 CE0STOC(注意COPA自动产生的结构和表名称命名规则是CE0-4+OC名称).

    CE0STOC:结构,用于COPA程序中定义内表/

    CE1STOC:保存actual line items.

    CE2STOC:保存plan line items

    CE3STOC:保存PSG info .获利分析段信息

    CE4STOC:

    CE4STOC_ACCT|CE4STOC_FLAG|CE4STOC_KENC意义读者可自己去研究.

    Operating Concern 在我们公司被翻译成 经营组织

    上述链接的文章要点:

    在建立经营范围时,系统会产生这样一个结构CE0****.

    包括以下结构和表:

    CE0****:结构,用于COPA程序中定义内表

    CE1****:获利分析实际数据

    CE2****:获利分析计划数据

    CE3****:保存获利分析段信息

    CE4****

    CE4****_ACCT:基于帐户的获利分析

    我们公司的表CE11000的意思其实是 CE1 1000 前面是统一的前缀,后面是1000我们公司自定义的经营范围的代码

    注:其中****表示获利分析模块的最高组织结构经营范围。COPA自动产生的结构和表名称命名规则是CE0-4+OC名称


    KE91KE31的区别:

    KE91是用来创建基于行项目的COPA报表(数据取自行项目表CE1XXXX和获利段表CE4XXXX,其中:XXXX为经营范围代码);

    KE31是用来创建获利能力段的报表(该类报表的特性只能取KEQ3/KEQ4中的可用的获利能力特性,若KEQ3中将某特性设置为不可用,则KE31中无法取到该特性的信息),其数据取自获利段级别的数据表CE3XXXX和获利段数据表CE4XXXXKE94KE34的区别与此相同。

    Sources for 'KE91' are all characteristicsthat you have in table CE1XXXX (where XXXX is your operating concern. It mightbe possible you don't have some technical characteristic but it is the concernand you should be able to use almost all characteristics of CE1XXXX.

    Go through this link which will give you anoverview on the table structure:http://help.sap.com/saphelp_46c/helpdata/en/74/e3a226e64e11d1a5300060087a7a87/content.htm


    COPA中常用的CE1XXXX/ CE2XXXX/CE3XXXX/ CE4XXXX的解释如下:

    CE1****: Table CE1**** stores actual line item data. This table stores allvalues—characteristic values(特征值),value field values(值字段), and theprofitability segment for all actual posting in CO-PA. As with the actual line items table, allcharacteristics, including technical T-type characteristics, are included andposted to in CE1****. You can create drill-down reports from the line itemtables. You can display line items in CO-PA, but it is a veryperformance-exhaustive and lengthy process. You should access line items onlywhen you absolutely need to view characteristics that are not part of thesegment level.

    如下图,可以看到:



    我找了一个文章 FICO模块系统配置文档 这个文章绝对是干货,我几乎在这篇文章中找到了所有的答案。

    简单地说,你最终关注的数据是透明表CE11000中的字段VV003,这个字段的学名叫做 值字段(Value Field),是管理会计(CO)的重要工作之一获利能力分析(PA)中的一个概念。你现在的问题是,表找到了,字段找到了,结构上没什么问题,你新创建的订单带来的收入也可以归集到对应的会计科目,但是新订单带来的收入无法归体现在获利能力分析上。这是最主要的。

    特征(Characteristic Values通常可理解为有固定数据的字段比如产品,物料等,而值字段(Value Field)通常对应的金额

    那么值字段(value field)是如何配置的呢?

    上面链接的文档其实都有说明,简单地说就是下图:

    1.维护经营关注点(Operating Concern)

    2.维护特性(Characteristic Values

    3.维护值字段(Value Field)

    4.分配值字段

    维护经营关注点 路径如下:



    输入经营组织代码1000 

    然后点击显示按钮


    你就能看到你之前需要了解的表格CE11000,值字段tab中,你可以看到vv003这是你觉得有问题的字段,你新创建的分销渠道和产品组对应的销售订单带来的收入,

    没有归结到CE11000这个表的VV003这个字段中去,现在有一点线索了

    上图中你看了“值字段” 这是个中文词,对应的英文实际上是 value field 。

    那么SAP的IMG中有没有对于 值字段的 设置呢?有


    看上图,维护值字段 (value field)


    输入经营范围 代码 点击 显示 按钮(眼瞎的人可以看看黄色的按钮,前面有个眼镜图标的那个,汉字“显示”,对,看见了吧)


    看见了吧BK,上面图中VV003就是你需要的字段 不含税 销售收入

    下面看看 特性值 (characteristic value)


    上面说的都是没问题的部分,因为现在不是字段不存在,是字段VV003中没有收集到 新增 销售范围(Sales Area) 带来的收入。

    下面看看 值字段 的分配





    上图是关键哦,虽然我删除了数据,但是大家可以看出来,实际上分配 值字段 的意义就是 把值字段和条件类型(Condition Type, Ctype)关联起来

    由于CO-PA建立完成值字段后,仍须定义其与各销售主档间的关系,否则这些值字段不会有值进入,且因为PA只能认列销售的两个时点,一是销售订单时,二是Billing,因此为求实际销售值能正确进入PA

    要维护 实际 销售订单 值 与 PA(获利能力分析) 的关系,各模块与值字段的连结是以条件类型为基础,在上页中可以看出,所有销售相关条件类型值字段 间的分配,日后有新条件类型产生时,必须 添加新条目,新条件类型(Condition Type)分配至值字段(Value Field)。

    最后给大家来个高清无码大图:


    看了上面那么多所谓的干货,实际上,自己提的问题,是错误的问题,因为我搞错了因果关系。

    最开始我以为 CE11000这个表中没有我需要的数据,但是COPA是正常的。我判断的依据是能够在KE30中找到新增的分销渠道和产品组,但是瞪着大眼没看见,对应的销售收入(不含税)是0啊

    我做了个试验,手动修改CE11000这个表,找到我新增销售范围对应的订单编号,为它在CE11000中的VV003字段填上数,再次运行KE30,COPA的结果,还是没有销售收入,现在我判断,CE11000这个表不可能是KE30的数据源。



    展开全文
  • 企业经营运营过程中,如何获取挖掘企业经营数据中的有用价值,并用于企业管理层分析决策?SAP中,可以试用获利能力分析模块(Controlling Profit Analysis下文简称COPA )作为对应的解决方案,本章将介绍重点...

    1.1    概述 (加作者微信索取无水印PDF完整版)

    在企业经营运营过程中,如何获取和挖掘企业经营数据中的有用价值,并用于企业管理层分析决策?在SAP中,可以试用获利能力分析模块(Controlling Profit Analysis下文简称COPA )作为对应的解决方案,本章将介绍重点介绍该功能。

    获利能力分析收集、合并、评估来自销售、财务、成本等模块的数据,通过灵活的报表工具展示经营结果,实现多维度的利润分析,例如哪些客户贡献了最大的利润,哪些产品的销量发生下滑等。同时基于将来一段期间的预计产品销量,结合成本费用的评估策略,出具计划利润分析报表,为战略决策提供完整的历史经营数据和可靠的计划评估数据,获利能力分析模块的整体流程如21‑1 所示。

    COPA获利能力分析【01数据结构和配置】

     21 1 获利能力分析-整体流程图

    COPA包含基于成本核算和基于科目两种模型。

            基于成本核算模型:使用值字段记录交易金额或数量,在数据传输到COPA时,通过匹配销售订单条件类型、会计科目等更新对应的值字段。

            基于科目模型:基于会计科目记录交易金额,两种方法比较如下。

    COPA获利能力分析【01数据结构和配置】

    由于基于科目的COPA需要从科目过账取得数据,限制了数据传输的途径,同时由于无法使用成本评估,在计划获利报表中无法计算贡献利润,一般建议使用基于成本核算的COPA模型。

    1.1    数据结构

    COPA的数据和报表都由特征和值字段构成,特征是数值分析的维度,如“产品”、“客户”等,值字段用于存储金额或数量,如“收入”、“销量”等,可以将若干特征组合实现多维度的获利分析。

    经营范围是COPA模块的最高组织单位,定义了包含哪些特征和值字段,一个经营范围可以跨越多个控制范围和公司代码,以实现跨组织的利润分析。

    COPA的主要数据表是在激活经营范围时自动生成的,如图 21‑2 所示。

    COPA获利能力分析【01数据结构和配置】

     21 2 保存并激活经营范围

    *XXXX即经营范围编号。

    其他模块的数据传输到COPA时,各数据表的更新逻辑如 图 21‑3 

    COPA获利能力分析【01数据结构和配置】

     21 3 数据表-更新逻辑

        更新获利能力段,如果该段已存在则不更新。

        创建COPA行项目,包括特征和值字段,每个传输到COPA的业务如销售开票、会计凭证过账等都会在该表生成行项目。

        更新获利能力段合计金额,例如第12行明细的获利能力段相同,则累计各值字段金额并更新到该表。

    注意在合计表CE3XXXX中没有具体的特征,而是基于获利能力段的合计,获利能力段中一般仅包含合计报表中常用的特征如组织单元、产品组、客户组等,由于不需要基于销售订单合计,所以段中不含销售订单,如本例。出具基于获利能力段的合计报表时系统直接访问CE3XXXX表,可以提高报表的性能,所以在维护段时需要谨慎考虑包含哪些特征,既提高报表效率,又不会产生过多的段,例如将销售订单放到段中将产生大量的段数据,使得合计数据的条目接近明细数据,失去了段的意义。

    1.1    相关配置

    在启用COPA之前,需要先预设好经营范围以及其包含的特征值,获利能力段等等。

    1.1.1       维护特征

    特征即数据分析的维度,SAP已经预设了标准特征,系统最多允许创建30个自定义特征。

    路径:IMGà控制à获利能力分析à结构à定义经营范围àKEA5 - 维护特征

    COPA获利能力分析【01数据结构和配置】

     21 4维护特征值

    点击“改变按钮,系统提示“注意:您正在处理客户间数据结构”,确认后进入画面如 图 21‑5 

    COPA获利能力分析【01数据结构和配置】

     21 5 新建/激活特征值

    显示为灰色无法修改的特征是标准特征,显示为白色可修改的特征为用户自定义特征,用户自定义特征必须以WW开头。

    无法从标准表的字段自动生成特性时,可自定义特性如图 21‑6 

    COPA获利能力分析【01数据结构和配置】

     21 6 创建特性

    1.1.1       维护值字段

    值字段用于存储金额或数量,自定义值字段必须以VV开头,系统最多允许创建120个值字段。

    路径:IMGà控制à获利能力分析à结构à定义经营范围àKEA6 - 维护值字段

    COPA获利能力分析【01数据结构和配置】

     21 7维护值字段

    点击修改按钮,系统提示“注意:您正在处理客户间数据结构”,确认后进入画面如 图 21‑8 

    COPA获利能力分析【01数据结构和配置】

     21 8 新建值字段

    1.1.1       维护经营范围

    路径:IMGà控制à获利能力分析à结构à定义经营范围àKEA0 - 维护经营关注点

    COPA获利能力分析【01数据结构和配置】

     21 9维护经营范围

    点击更改按钮进入数据结构维护画面,设置该经营范围中包含的特征和值字段。

    COPA获利能力分析【01数据结构和配置】

     21 10设置特征和值字段

     

    切换到值字段标签。

    COPA获利能力分析【01数据结构和配置】

     21 11值字段选择

    返回经营范围画面并切换到属性标签。

    COPA获利能力分析【01数据结构和配置】

     21 12经营范围-属性维护

            营运相关货币:COPA的默认评估货币,在基于成本核算的COPA中所有实际数据都使用该货币评估,可以将其视为COPA中的本位币。

            公司代码货币:选中该标志则系统同时使用公司代码货币存储值字段,基于公司代码货币的值字段数值与FI金额保持一致。

            利润中心评估:从利润中心的角度评估获利能力,即包含了内部利润的平行评估,可以出具利润中心独立核算的获利能力分析报表。

    *关于平行评估,请参考“19.7平行评估与转移价格”章节。

            会计年度变式:经营范围的会计年度变式必须与控制范围和公司代码一致。

    切换到环境标签。

    COPA获利能力分析【01数据结构和配置】

     21 13经营范围-激活

    经营范围创建成功后,系统自动创建数据表CE1XXXX~CE4XXXX

     

    路径:IMGà企业结构à分配à控制à把控制范围分配给经营范围

    COPA获利能力分析【01数据结构和配置】

     21 14控制范围分配给经营范围

     

    路径:IMGà控制à一般控制à组织结构àOKKP - 维护成本控制范围

    COPA获利能力分析【01数据结构和配置】

     21 15维护成本控制范围

    1.1.1       维护获利能力段

    获利能力段是若干特征的组合,SAP建议将常用的分析维度组合成段,而将内容频繁变化的特征排除在外,如销售订单编号等,否则会导致产生大量的段,使系统性能下降。

    路径:IMGà控制à获利能力分析à结构àKEQ3 - 定义获利能力段特性(段层特征)

    COPA获利能力分析【01数据结构和配置】

     21 16维护获利能力段

            没有使用:该特征不含在段中。

            基于成本核算:仅在基于成本核算的段中包含该特征。

            基于成本+基于科目:在基于成本核算和科目的段中包含该特征。

            例外:对于包含在段中的特征,可以点击该按钮,设置符合某些特定条件的特征被排除在段中,但该特征仍然存在于行项目数据中。

    展开全文
  • 如果面试问起,就说底层主要靠volatileCAS操作实现的。 五.CAS机制 Compare And Swap CAS操作是一个原子操作,由一条CPU指令完成,使用了3个基本操作数,内存地址V,旧的预期值A ,要修改的新值B 需要更新一个...

    可以说,学姐给我的这份文档真的把我的知识查漏补缺,面试问到了好多,值得收藏。

    并发编程

    .Executor

    为什么使用线程池:手动创建线程耗费性能,不利于管理。

    首先创建线程池有两种方式:使用Executors工厂来创建ThreadPoolExecutor这类自定义线程池。

    1. 使用Executors工厂来创建

    Executors是一个类,用来创建线程池,常用的有四种线程池

    1.newFixedThreadPool 创建一个可重复固定的线程数的线程池

    2.newCachedThreadPool 创建一个可缓存的线程池,调用execute将重复用以前构造的线程(如果当前线程可用)。如果没有可用线程则创建新的线程并加入到池中。终止并从缓存中移除那些已有60s未被使用的线程。

    3.newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

    4. newScheduledThreadPool 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

    使用示例:

     

     

     

    2. 使用ThreadPoolExecutor这个类自定义创建线程池

     

     

    .ThreadLocal

    https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/6782674c-1bfe-4879-af39-e9d722a95d39.png

     

    .中断线程

    1.线程执行完毕会自动结束

    2.在线程处于阻塞,期限等待火无期限等待时,调用线程的interrupt()会抛出interruptedException异常从而提前终止线程

    3.若没有处于阻塞状态,调用interrupte()将线程标记为中断,此时在调用interrupted()判断线程是否处于中断状态,来提前终止线程。

    4.线程池调用shutdown()等待所有线程执行完毕之后关闭

     

     

    .Volatile关键字的理解

    两层语义:1.保证不同线程对该变量的内存可见线

              2.禁止进行指令重排序

     

    内存语义:

    读这个变量的时候,JMM会把本地内存中的变量设置为无效,从主内存中取;

    写这个变量的时候,JMM会把本地内存变量刷新到主内存当中

    实现:添加Volatile关键字会在汇编代码中多处一个kock前指令,也就是内存屏障

     

    .Synchronized关键字

    1synchronizedJava中的关键字,是一种同步锁。它修饰的对象有以下几种:

        1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

        2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

        3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

    4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

     

    2Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。

    3Synchronized 原理

        在编译的字节码中加入了两条指令来进行代码的同步。

        monitorenter :

        每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

        1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

        2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

        3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

        monitorexit:

        执行monitorexit的线程必须是objectref所对应的monitor的所有者。

        指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

         通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

    4Synchronized的优化

    (4)synchronized与Lock的区别

        lock是一个类,主要有以下几个方法:

        lock():获取锁,如果锁被暂用则一直等待

        unlock():释放锁

        tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true

    tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间

    https://note.youdao.com/yws/public/resource/cc25598bbaff18a55569c256de4cf1c6/xmlnote/10A95AB8238442569108FC93755C91FF/1701

     

        (1)Lock的加锁和解锁都是由java代码实现的,而synchronize的加锁和解锁的过程是由JVM管理的。

        (2)synchronized能锁住类、方法和代码块,而Lock是块范围内的。

        (3)Lock能提高多个线程读操作的效率;

    (4)Lock:Lock实现和synchronized不一样,后者是一种悲观锁,它胆子很小,它很怕有人和它抢吃的,所以它每次吃东西前都把自己关起来。而Lock底层其实是CAS乐观锁的体现,它无所谓,别人抢了它吃的,它重新去拿吃的就好啦,所以它很乐观。如果面试问起,你就说底层主要靠volatile和CAS操作实现的。

    .CAS机制

    Compare And Swap

    CAS操作是一个原子操作,由一条CPU指令完成,使用了3个基本操作数,内存地址V,旧的预期值A ,要修改的新值B

    需要更新一个变量的时候,只有当内存地址V中的值和预期值A相等的时候才会修改为新值B ,如果失败则自旋,重新尝试

    问题:

    1.ABA 解决办法,加一个版本号,只有当版本号和预期值都符合的时候才修改

    2.不能保证代码块的原子性,CAS机制知识保证一个变量的原子操作

    .JMM的理解

    JMM的核心是找到一个平衡点,在保证内存可见性的前提下,尽量的放松对编译器和处理器重排序的限制。

     

    可能会产生缓存一致性的问题:

    1.总线锁定

    2.缓存一致性协议

     

    三大特性:原子性,可见性,有序性

     

     

     

    .JVM的锁优化

    1.自旋锁和自适应自旋锁

    线程挂起和恢复都需要很大的性能开销,很多共享数据的锁定状态只持续很短的时间,为这段时间挂起恢复不值得,所以可以让现场进入一个自旋状态,但仍占用cpu的时间,默认是10次,超过10次则采用传统的方式挂起线程。自适应自旋锁会根据上次在这个锁上自旋的时间调整自选次数

     

    2.锁消除

    检测到对局部变量加锁,会自动将锁消除,如在一个方法里面有sb = s1 + s2 + s3以前会转化为StringBuffer(线程安全,有加锁)的append操作,优化之后会转换成StringBuilder的sppend操作

     

    3.锁粗化

    对一个对象反复加锁解锁,如上述的append方法,编译器会优化,只在最外层加一个锁

     

    4.轻量级锁

    对象有一个对象头,对象头分两部分区域,一部分存储子树的hashcode,GC分代年龄,锁标志位等,官方成为mark word,另一部分用于存储指向方法区对象类型的指针;

    轻量级锁的执行过程是代码进入同步块的时候,如果当前对象没有被锁定(锁标志为是01)则先在线程的栈帧中建立一个锁记录空间(lock record)将对象的mark word拷贝过来,然后利用cas操作尝试将对象的markword更新为指向lock record,如果成功则当前线程拥有该对象锁,并且将锁标志位从01改为00,如果更新失败则会检查当前对象markword是否指向当前线程的栈帧,如果是则直接进入同步块执行否则说明当前的对象锁已经被其他线程占有。如果有两条以上的线程竞争这个锁,那就会膨胀成重量级锁,锁标志为变成10;

     

    5.偏向锁

    锁对象第一次被线程获取的时候,会将当前的锁标志设置为偏向锁01状态,并使用cas操作将当前线程的id记录在markword当中,如果成功那么在以后的操作当中不需进行任何操作就可以进入同步代码块,当有另外一个线程获取当前锁的时候,偏向模式就宣告结束,撤销偏向锁之后恢复到未锁定或者轻量级锁状态

     

    JAVA基础

    String

    1.string s1 = “aaa” 与 String s1 = new String(aaa);

    前者会在Stringpool中创建一个对象,如果Stringpool中已经有了,则直接引用

    后者会在Stringpool和堆中分别创建一个对象,如果StringPool中已经有了则只创建一个对象

     

    2.调用s1.integer()方法,可以将字符串放入StringPool中,如果已经存在则直接返回这个对象

     

     

    继承

    1.几种修饰符的访问权限范围

     

    2. ==  equals  hashcode的关系

    1.==是比较两个对象的地址是否相等

    2.equals默认和 == 一样也是比较地址,也可以根据自己的需求来重写什么叫做相等

    3.hashcode 是根据对象的地址来返回一串int型数字,如果对象不一样的话返回的值也不一样

     

    3.为什么重写equals的同时也要重写hashcode

    首先必须满足如果equals相同,那么hashcode也必须相同,如果hashcode不相同,那么equals必须不相同的原则,否则会导致该类无法与基于散列值的集合类(hashmap , hashset ,hashtable)结合正常运行

     

    因为对于这些集合在进行重复性判断时,是先用hashcode判断,如果相同在用equals判断

     

     

    4.hash冲突的解决方法

    1.开放地址法(线性探测法,二次探测法)

    2.在散列函数法

    3.链地址法

     

    5.hashtablehashmap的区别

    1.hashmap线程不安全,hashtable线程安全

    2.hashmap最多允许一个键为null,而hashtable不允许

    3.hashtable中默认的hash数组大小为11,增加的方式是old*2+1,而hashmap默认是16,肯定是2的指数

    4.计算hash的方法不一样,hashtable是直接用hashcode,而hashmap使用了key的高16位和低16位做异或运算

     

    Linkedhashmap继承于hashmap,可以按顺序读取,底层使用的是双向链表

    TreeMap实现了sortmap接口,可以对元素进行排序,底层使用的红黑树

     

    7.java BIO NIO AIO 序列化

    字节操作:使用inputStream和outPutSream实现,字节是传输和存储的最小单位

    字符操作:inputStreamReader:将字节流解码成字符流

    OutPutStramWriter:将字符流编码成字节流

    对象操作:序列化就是将对象转换成字节序列,方便存储和运输,两个用途:

    1.把对象的字节序列永久地保存到硬盘中,通常存放在一个文件里

    2.在网络上传送对象的字节序列

    在很多应用中,需要对某些对象进行序列化,让他们离开内存空间,入住到物理磁盘方便长期保存,比如session对象,当有大量用户并发访问的时候可能会出现10万个session对象,内存吃不消,此时就需要将这些session先序列化到硬盘中,等要用的时候在把对象还原到内存中。

    当两个进程在进行远程通信的时候,彼此可以发送各种类型的数据,无论是何种类型的数据,都会以二进制的序列在网络上传送。发送方需要把这个java对象转换成字节序列,才能在网络上传送;接收方则需要把字节序列恢复成java对象。

    Io与NIO的区别:

    1.NIO是非阻塞的

    2.NIO是面向缓冲区的,IO是面向流的

     

    AIO:异步非阻塞

     

     

     

    8.static关键字的作用

    一.修饰变量:

    1.静态变量在类加载的时候被创建并初始化,只被创建一次(类加载只进行一次),可以修改

    2.静态变量属于整个类而不属于某个对象。

     

    二.修饰方法

    1.可以通过类名来访问,不需要创建对象来访问,可以用来实现单例模式

    2.静态方法只能调用静态方法和静态变量

     

    三.修饰静态代码块

    在类加载的时候执行,且只执行一次

     

    9.单例模式

    实现:

    1.为什么要用判断双重:
    因为可能有两个线程都执行完了第一个if语句,如果没有第二重判断,那么当其中有个线程执行完synchronized里面的语句之后,另外一个线程跟着也会执行,这样就达不到单例模式的效果

     

    2.第一重判断去掉也可以实现,为什么不去掉

    这个设计性能问题,因为

    参考:https://qinjiangbo.com/mechanism-of-double-locking-check.html

     

    10.this与super关键字


     

     

    9.java中的多态

    分为两种:

    1.编译时多态:体现在重载(方法名相同而参数不同),在编译时期就根据传入的参数确定好调用哪个方法;

    2.运行时多态:体现在方法的重写。在运行时期判断引用类型的实际类型根据实际的类型调用其相应的方法;

    当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,引用变量类型决定可调用的方法。如果子类中没有覆盖该方法,那么会去父类中寻找。

     

    参考链接:https://www.runoob.com/w3cnote/java-polymorphism.html

     

    10.接口类和抽象类的异同

    区别:

    1.抽象类可以有非抽象方法,但接口只能有抽象方法

    2.接口中的成员变量默认修饰为public static final(高度抽象的模版,所以这些都是提取出来的不变特征),方法默认修饰为public abstract。抽象类中的成员变量可以被不同的修饰符来修饰

    3.类可以实现多个接口但只能继承一个抽象类

     

    相同:

    1.不能被实例化

    2.派生类都必须实现未实现的方法

     

    11.instanceof

    用来判断一个对象是否是一个类的实例

    12.各种排序算法复杂度及稳定性

    1.java底层如何实现排序的

    Java中Arrays.sort使用了两种排序方法,快速排序和优化归并排序。

    快速排序主要针对基本的数据类型(int short long)排序,而归并排序用于对象类型的排序

     

    如果数据量小于60会使用插入排序,插入排序是稳定的

     

    13.java中的堆和栈

    栈:主要用于存储局部变量和对象的引用变量,每个线程都有一个独立的栈空间,所以线程之间是不共享数据的;

    栈的特点是存取速度快,但所存数据的大小和生存期必须是确定的(编译后就已经确定大小)

     

    堆:堆中主要存储实例化的对象和数组。线程共享。堆的优势是可以动态的分配内存大小,生存期也不必事先告诉编译器,但缺点是存取速度较慢

     

     

    Linux基本面试题:

    Ls:用于显示指定工作目录下的类容,不会列出详细信息

    Ll:会列出当前文件目录的详细信息,含有时间,权限,大小等

    Cd:用于切换到目标目录

    Mkdir:用于简历当前目录的子目录

    rm-r 删除的文件或者目录,需确认

    rm –rf同上但无需确认

    cp:复制文件 若复制目录则必须加上-r

    数据库MySQL

    .索引

    1.B树,B+树,以及两者的区别

    B树是一种多路平衡查找树,其每一个节点都存储Key和data

    B+树是B树的一个变种,叶子节点存储data,非叶子节点只存储key,B+树的叶子节点增加了顺序访问指针,每一个叶子节点都可以访问到他的下一个叶子节点

     

     

    区别:

    1.B+树种只有叶子节点会带有全部信息,非叶子节点只起到索引的作用,二B树的所有节点都带有全部信息,B+树的每一层节点都会再次出现在下一层节点上

    2.B+树种所有叶子节点都是通过指针连在一起,B树则没有

     

    2.索引的优点和缺点

    优点:可以加大检索速度

    缺点:创建和维护索引需要耗费时间

     

    3.Mysql为什么选择B+

    Mysql数据本质上是放在外部存储的,B+树是为了加快读取速度二设计的一种数据结构

    1.可以减少i/o次数,只有叶子节点才存储数据,非叶子节点存储索引,这样一次读取到内存的关键字增多,相对i/o次数也就减少(根据区别一

    2.能够提供稳定高效的范围扫描,因为所有的叶子节点都互相连接(根据区别二

     

    4.索引越多越好吗?

    索引可以提高select的效率,但是也降低了insert和updata的效率,因为插入和更新的时候可能会重建索引,索引怎么建索引要慎重考虑。

     

    5.索引分类

    1.B+树索引:以b+树作为数据结构的索引

    2.hash索引:能以O(1)的时间复杂度查找,但失去了有序性,innodb有一个自适应哈希索引,当这个索引值被频繁使用时会在b+树上创建一个哈希索引

    3.全文索引:用于查找文本的关键词,中文需要由中文分词插件

    . MySQL优化

    一.MySQL的优化,主要分为索引的的优化,sql语句的优化,表的优化。同时可以使用缓存增加效率

    1.索引的优化

    只要列中含有null,最好不要再此列设置索引

    对于经常在where语句中使用的列,最好设置一个索引

    对于like语句,以%或者-开头的不会使用索引,以%结尾会使用索引

     

    二.sql语句的优化

    查询优化要尽量避免全表扫描

    查询时能不用*就不用*,尽量写字段名

    . MySQL常问问题

    1.数据库如何应对大规模的写入和读取

    (1)使用NoSQL,通过降低数据的安全性,减少对事物的支持,减少复杂查询的支持来获取性能的提升;但有些场合NoSQL无法满足要求

    (2)分库分表:

    水平切分:不修改数据库的表结构,通过对表中的数据拆分而达到分片的目的,一般水平切分在查询的时候可能会用到union操作(多个结果并)

    可以根据hash或者日期来进行分表

     

    垂直切分:修改表结构,按照访问的差异将某些列拆分出去,一般查询数据的时候可能会用到join操作;把常用的字段放在一个表中,不常用的放在一个表中;把字段比较大的比如text字段拆出来放在一个表中。

     

    分库:分表能够解决数据量过大带来的查询效率下降问题,但是却无法给数据库的并发处理能力带来质的提升;分库可以对关键字取模的方式来对数据访问进行路由;https://note.youdao.com/yws/public/resource/96920e055ee2c654ada64b031cefec78/xmlnote/4A29F29BE4E14CFBB1B746D67A6018AC/9130

     

     

     

    (3)读写分离

    读写分离是在主服务器上修改数据,数据也会同步到从服务器上,从服务器只能提供读取,不能写入,实现备份的同时也实现了数据库的性能优化

    https://note.youdao.com/yws/public/resource/96920e055ee2c654ada64b031cefec78/xmlnote/747008E97868421DA096C6672EBDE002/9136

    如何保证数据一致性:

    (1)主节点

    保证事务每次提交之后,要确保binlog都能刷新到磁盘中,只要有了binlog,innoDB就有方法恢复数据,不至于导致主从复制的数据丢失

    (2)从节点

        开启 relay log 自动修复机制,发生 crash 时,会自动判断哪些 relay log 需要重新从master 上抓取回来再次应用,以此避免部分数据丢失的可能性。

     

    2.数据库事务及其隔离级别

    事务的特性:ACID

     

    事务在并发的时候,隔离性很难保证主要可能出现下面这些问题

    脏读:一个事务读了另外一个事务未提交的数据,如果另一个事务回滚则会发生脏读

    不可重复读:一个事务前后读取同一行数据,如果在这个过程中有其他事务修改了此数据则会发生不可重复读

    幻读:一个事务前后读取范围的时候

     

     

     

    事务隔离级别:

    MySQL实现事务是基于undo/redo日志实现的:
    undo日志记录修改前的状态,ROLLBACK基于UNDO日志实现;

    REDO日志记录修改后的状态,事务的持久性基于REDO日志实现

     

    两种解决脏读、不可重复读、幻读的方案:

    MVCC(性能较高,但读的可能是历史版本)

    1.版本链:对于每一行的数据,在undo日志中,总会记录每个版本记录以及对应的事务id,

    2.readView:

    核心问题:当前版本链中哪个版本对当前事务可见

    Readview包含的内容:{当前活跃的事务id,下一个应该分配的事务id,当前自己的事务id},根据当前读的版本事务id和这个readview对比,如果是活跃的或者大于下一个应该分配的事务id则说明当前版本对此事务不可见,应该前读一个版本,依次类推直到找到可见的版本

    提交读:每次读取数据前都会生成一个readview

    可重复读:在第一次读取时句时生成一个readview

     

     

    锁(性能不高,但读的是最新版本):

     

     

     

    MyISAM和innoDB的区别

    1.innodb支持行锁,myisam不支持行锁

    2.innodb支持事务,myisam不支持事务

    3.innodb支持回滚和安全回复,myisam不支持

    4.innodb的索引就是数据,myisam的索引只存储了主键和行号,还需要根据行号去查找相应的记录

    5.innodb更适合写密集的表,myisam更适合读密集的表

    计算机网络

    1.tcp和udp的区别:

    Udp:无连接,尽最大可能交付,没有拥塞控制流量控制

    Tcp:面向连接,可靠交付,有拥塞控制和流量控制

     

    2.输入一条url,整个过程:

    1.DNS解析,获取ip地址(本机,本地域名服务器,根域名服务器,顶级域名服务器,权限域名服务器)

    2.建立TCP连接

    3.浏览器发出http请求

    4.服务器进行响应

    5.TCP连接释放

    6.浏览器渲染

     

    3.为什么是三次握手,四次挥手

    三次握手:防止之前滞留的连接请求再次到达服务端

    四次挥手:因为tcp是全双工模式,客户端停止发送请求之后,服务端也要停止发送请求

     

    4.time_wait存在的原因,时间是多少(两倍的报文最大存活时间)

    1.确保客户端发送的最后一个报文能被收到,服务端可以正常关闭。

    2.让所有报文都在网络中消失,时间是两倍的最大报文存活时间。

     

    5.tcp的可靠传输靠什么:

    超时重传:如果已经发送的报文在超过时间内没有被确认,那么就重新发送这个报文

     

    6.Tcp的滑动窗口

    发送方和接收方都有一个滑动窗口

     

    7.TCP流量控制

    流量控制是为了控制发送方的发送速率,保证接收方来得及接收

    通过滑动窗口来控制,根据报文知道对方窗口的大小,然后根据窗口大小来控制发送速率

     

     

     

    8.TCP拥塞控制

    如报文过多,会导致超时重传,又会导致网络更加阻塞

    https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/910f613f-514f-4534-87dd-9b4699d59d31.png

    1.慢开始和拥塞避免:

    慢开始:设置初始的报文数量为1;

    拥塞避免:设置一个阈值,当报文数超过这个阈值的时候每次,报文每次加一,如果出现超时领阈值等于当前报文的一半,重新执行慢开始

     

    快重传:如果收到3个确认报文,则重传丢失的那个报文

    快恢复:这种情况令阈值等于当前报文的一半,并令当前发送的报文数等于阈值,因为没有出现网络阻塞

     

     

    http各个版本的区别

    http0.9 : 仅支持GET请求,仅能访问html资源

    http 1.0 :增加了post和head请求

    http1.1 : 增加了长连接,一个tcp连接可以发送多个http请求,新增了put,patch,delete请求

    http2.0 : 增加了双工模式,不仅客户端能发送多个请求,服务端也能处理多个请求

    Redis

    1.redis的数据淘汰策略

    当redis内存数据大小达到一定的大小时,就会施行数据淘汰策略,主要有六种策略

     

    2.数据库和缓存的数据一致性

    2.1 mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

    根据数据淘汰策略,先算一下这20W的数据大概占多少内存,然后设置redis的内存,启用从所有数据集中挑选最近最少使用的淘汰策略

     

    2.2 redis缓存和mysql数据库同步

     

     

     

    3.Redis持久化

    1.RDB持久化(redis默认方式)

    将某个时间点的所有数据都存在硬盘中,如果发生故障将丢失最后一次创建快照的数据

    触发RDB快照的条件:在指定的时间间隔内,执行指定次数的写操作

    2.AOF持久化

    所执行的每一条指令,都会记录到appendonly.aof文件中,redis会按照策略将指令存入硬盘中。当redis重启的时候会根据日志文件的内容将写指令从前到后执行一次完成数据恢复的功能

     

     

    Java异常体系

    http://assets.tianmaying.com/md-image/c887bccaa4b144622c82685097908f3d.png

    Error:主要是虚拟机产生,与代码编写者无关,例如:outOfMemoryError

     

    Exception:主要有运行时异常和io异常

    运行时异常:数组越界,空指针异常

    Io异常:找不到文件

     

    发生oom的可能区域:

    除了程序计数器都有可能发生

    虚拟机栈:当jvm尝试去扩展栈空间失败时可能会抛出oom异常

    堆:堆中对象堆积起来无法释放

    方法区

     

     

    四种引用类型:

    主要体现在对象不可达性和对垃圾回收的影响:
    强引用:只要有强引用指向一个对象,就表面对象还活着,垃圾回收期不会碰这种对象

    软引用:只有当jvm认为内存不足的时候才会去试图回收这些对象

    弱引用:不能使对象豁免垃圾回收,只是提供一种对象的访问途径

    虚引用:不能提供他访问对象,仅仅提供一种确保对象呗finalize之后做某些事情的机制

     

     

     

    JAVA虚拟机是如果加载类的

    1.加载:主要是用类加载器(启动类加载器,扩展类加载器,应用类加载器)来查找对应类的字节流

    双亲委派的好处:由于类随着类加载器一起有一种优先级的层级关系,从而使基础类得到统一

     

    2.链接

    验证:确保字节流的安全性,不会对虚拟机造成危害

    准备:为类的静态字段分配内存

    解析:将符号引用解析成实际引用

     

    3.初始化:调用<clinit>方法,为静态变量赋与实际的值,执行静态代码块

    运行时数据区域:

     

     

     

    运行时内存区域

    1.程序计数器:记录正在执行虚拟机字节码的指令地址

    2.java虚拟机栈:主要存储局部变量表

    3.本地方法栈

    4.堆:存储对象的地方,有新生代和老年代

    5.方法区:存储类信息,常量,静态变量等信息

     

    内存分配策略:

    1.对象优先在eden区域分配

    2.大对象直接进入老年代

    3.长期存活的对象进入老年代:会有一个年龄计数器,达到指定的阈值就会进入老年代

     

    FullGC触发条件:

    1.调用system.gc()

    2.老年代空间不足

    3.minor GC时老年代空间分配担保失败

    展开全文
  • 谁能告诉我这科的理论哪可以实用呀?搞不懂,只能收藏一下包不挂科
  • 说你懂计算机网络,那这些都知道吗

    万次阅读 多人点赞 2019-12-11 21:43:49
    今天的因特网无疑是有史以来由人类创造...随着5G时代的到来,万物互联也越来越称为可能,这里推荐一下 尤瓦尔·赫拉利 的《未来简史》,这个人的格局很高,他书中描述的未来也越来越成为现实,他写的文字能让感觉...
  • 关于 Spring AOP (AspectJ) 该知晓的一切

    万次阅读 多人点赞 2017-02-21 08:00:47
    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ... 出自【zejian的博客】 关联文章: ...关于 Spring AOP (AspectJ) 该知晓的一切本篇是年后第一篇博文,由于博主用了不少
  • 系统服务级别需要达到千万级用户量在线访问的要求不仅对整个研发团队或是运维团队带来了巨大的挑战,测试团队也不能独善其身,真得可以证明系统服务具备这样的能力吗?本文为了破局,以测试需求为根本,试图从测试...
  • 当时全网搜中文资料,陆续失望地发现竟然真的没有讲得清楚的博文,发现基本是把李航老师书里或CRF tutorial等资料的文字论述公式抄来抄去的。当然,没有说别人讲的是错的,只是觉得,要是没有把东西说的让读者看...
  • 电流集中导体的“皮肤”部分,也就是说电流集中导体外表的薄层,越靠近导体表面,电流密度越大,导线内部实际上电流较小。 结果使导体的电阻增加,使它的损耗功率也增加。这一现象称为趋肤效应。 1.2 原因:可...
  • 一个成功的RIA技术需要满足的条件

    千次阅读 2007-05-05 20:30:00
    不要将自己完全局限现有技术的能力范围内,来跟我一起预测一下未来技术的发展趋势,看看一种理想的RIA技术应该满足哪些条件。我先来开个头。1. 与服务器的交互方式必需首先支持异步的交互。异步的交互才不会打断...
  • 这些天,抽空读了一下人工智能基础(高中版),觉得作为高中科普教材,还是非常不错的,五星好评推荐。 下面会针对每一章的内容,依据兴趣等...这是medium.com上发布的文章列表,habr.comjqr.com。图标是可点击...
  • 一直不觉得这是一个问题,当连续两次诸友谈起这个问题而争执不下时,我突然意识到这应该是一个大问题。于是想当然地希望能集思广义,与更多人一起探讨这个问题的想法。同时也希望这里与大家的讨论结果,会带给己...
  • 应该掌握的 7 种回归模型!

    万次阅读 多人点赞 2018-07-19 21:26:56
    线性回归逻辑回归通常是人们学习...这篇文章中,我将以简单的形式介绍 7 中最常见的回归模型。通过这篇文章,我希望能够帮助大家对回归有更广泛全面的认识,而不是仅仅知道使用线性回归逻辑回归来解决实际...
  • 管理项目范围主要在于定义控制哪些工作包括项目内,哪些不应包括项目内。 ✓ 产品范围——某项产品、服务或成果所具有的特性功能 ✓ 项目范围——为交付具有规定特性与功能的产品、服务或成果而必须完成的...
  • 中国计算机学会(英文名为China Computer Federation,简称CCF),是由从事计算机及相关科学技术领域的科研、教育、开发、生产、管理、应用服务的个人及单位自愿结成、依法登记成立的全国性、学术性、非营利学术...
  • 希望能够节省的时间,对有所帮助。 2019 年最佳机器学习深度学习书籍的名单: 《深度学习》by Ian Goodfellow, Yoshua Bengio, Aaron Courville 《机器学习》 by 周志华 《统计学系方法(第二版)》by 李航 ...
  • 管理是一门水到渠成的学问,用不着从0开始拼命去追求,需要做的是35岁左右训练自己的情商、交际能力、带队能力,把自己的人际关系再扩大稳固一些,慢慢从纯技术路线转变成“技术+管理”的路线,最出名的莫过于...
  • 程序员职业软件资格的证书考试,简称程序员软考。   ...这是由人力资源社会保障部工业信息化部领导下的国家级考试,其目的是,科学、公正地对全国计算机与软件专业技术人员进行职业资格...
  • 应用层面,区块链的安全、透明、高效3大优势,使其特别有助于规范互联网金融的发展,以及促进物联网共享经济的普及与创新;资本市场,采用分布式数据库智能合约还可以大幅减少人工核对工作,为金融机构节省...
  • 对讲机的功能与使用频率范围

    万次阅读 2016-06-29 13:54:31
    对讲机的频率范围日常对讲机的使用中,根据中国无线电管理委员会规定,对讲机频率一般做如下划分: 专业对讲机:V段136-174MHZ;U段400-470MHZ; 武警公安用:350-390MHZ; 海岸用:220MHZ; 交通信号灯...
  • 可以像使用常规台式计算机一样树莓派上创建一个非常酷的工程。例如,可以用树莓派搭建自己的家用云存储服务器。树莓派用什么语言编程?树莓派用python来进行编程。树莓派项目的一个核心思想是Python编程语言...
  • 007数据分析能力:业务题

    千次阅读 2019-09-05 16:39:35
    如果现在有个情景,我们有一款游戏收入下降了,怎么分析 1)两层模型:细分用户、渠道、产品,看到底是哪里的收入下降了 2)指标拆解:收入=玩家数量活跃占比付费转化率付费次数客单价 进一步细分,如玩家数量=老...
  • 如果也23岁

    万次阅读 多人点赞 2014-10-15 14:01:21
    分享一下这篇文章: 23 岁那年正处在哪个状态?...22岁的时候我毕业,同时第二年准备考研,结果因为压力太大,期望太高,又失利了,但是我依然满怀信心憧憬  我23岁那年四月,当我深爱的女孩(这之
  • 但同样是考察项目范围管理,第二小问中也会出现不同的考试偏重考察视点。只有掌握好每个子过程的每个知识点,才是以不变应万变的最佳方法。 2005年上半年考试,偏重考查范围管理需求管理概念的区别于联系。 .....
  • 最近调试 前后端分离的请求测试,遇到了一个406错误, 无法接受,于是开始了人肉搜索406最后 还是HTTP头部信息里的Accept:application/json 这个Accept 导致的, 后端设置了只允许application/json 这种形式,so...
  • 上网不受限制

    万次阅读 2011-06-08 11:37:00
    现在公司上网真不爽,这个受限、那个打不开、下不了......看着就烦,如是就有了一个念头“突破”,TNND看他怎么限制我。下面就跟大家推荐一篇文章吧!讲的挺详细的:局域网用户的限制反限制技巧分类:LAN and WAN...
  • (一)什么是服务器并发处理能力 (二)有什么方法衡量服务器并发处理能力 1.吞吐率 2.压力测试 (三)怎么提高服务器的并发处理能力 1,提高CPU并发计算能力 (1)多进程&多线程 (2)减少进程切换...
  • 杀毒软件防火墙的区别

    万次阅读 多人点赞 2015-03-24 16:17:24
    虽然两者的共同目标都是保护电脑的安全,都是杀“毒”,(相对于杀毒软件工作范围中的病毒来说,防火墙要把互联网中任何对 PC 有威胁的“毒”程序隔离,这也是一种“毒”),但是在工作区域和在保卫电
  • 平时使用手机连接无线网络的时候,一定看到过这样的安全提示:不要连接陌生的 Wi-Fi。也一定看过很多这样的报道:某先生 / 女士因为使用了...但是,工作中,员工服务器通常接入的也是同一个网络,那员工是不是就.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 127,437
精华内容 50,974
关键字:

在你能力和条件允许的范围