精华内容
下载资源
问答
  • gbdt lr

    2019-06-18 22:02:51
    gbdt lr gbdt+lr是facebook提出在线广告模型,我们知道LR之前在广告和推荐系统由于其快速的计算而被广泛使用,使用由于lr是线性模型,其模型表现能力不强,需要做大量的特征工程。 facebook提出提出使用决策树进行...

    gbdt lr

    gbdt+lr是facebook提出在线广告模型,我们知道LR之前在广告和推荐系统由于其快速的计算而被广泛使用,使用由于lr是线性模型,其模型表现能力不强,需要做大量的特征工程。

    facebook提出提出使用决策树进行特征embedding。

    为了提升线性分类器的准确度,有两种方法进行特征变换:

    1. 对于连续特征。先进行离散化bin,然后把bin的编号作为离散型特征。这样的话,线性模型可以分段的学习到一个非线性的映射,在每一段内的映射是不变的。另外,对于bin边界的学习非常重要

    2. 对于离散特征。做笛卡尔积,生成的是tuple input features。笛卡尔积穷举了所有的特征组合,其中也包含部分没用的组合特征,不过可以筛选出来。
      提升决策树(boosted decision tree)就可以很方便很好的实现上面我们说的这种非线性和tuple特征变换。对于一个样本,针对每一颗树得到一个类别型特征。该特征取值为样本在树中落入的叶节点的编号。举例来说:
      在这里插入图片描述
      上图中的提升决策树包含两棵子树,第一棵树包含3个叶节点,第二棵树包含2个叶节点。输入样本x,在两棵树种分别落入叶子节点2和叶子节点1。那么特征转换就得到特征向量[0 1 0 1 0]。也就是说,把叶节点编号进行one-hot编码。
      那么, 怎么样直观的理解这种特征变化:

      • 看做是一种有监督的特征编码。把实值的vector转换成紧凑的二值的vector。
      • 从根节点到叶节点的一条路径,表示的是在特征上的一个特定的规则。所以,叶节点的编号代表了这种规则。表征了样本中的信息,而且进行了非线性的组合变换。
      • 最后再对叶节点编号组合,相当于学习这些规则的权重。

    核心思想

    1. 数据更新

    由于推荐和广告等相关的问题,是一个动态的环境,需要对模型进行实时更新,所有对于lr进行在线学习和更新,gbdt可以每天或者几天更新一次。

    1. 在线学习的学习率如何设置

    一般情况有很多学习率更新的方法,可以根据当前系统进行实验得到最好的学习率设置策略。论文中给出一下几种方法:

    • Per-coordinate learning rate:
      ηt,i=αβ+j=1tj,i2 \eta_{t,i}=\frac{\alpha}{\beta+\sqrt{\sum_{j=1}^{t}}\bigtriangledown_{j,i}^2}
      其中,α\alpha, β\beta是两个超参数.
    • Per-weight square root learning rate:
      ηt,i=αnt,i \eta_{t,i}=\frac{\alpha}{\sqrt{n_{t,i}}}
      其中,nt,in_{t,i}是特征i所有实例的前t次的总和。
    • Per-weight learning rate:
      ηt,i=αnt,i \eta_{t,i}=\frac{\alpha}{n_{t,i}}
    • Global learning rate:
      ηt,i=αt \eta_{t,i}=\frac{\alpha}{\sqrt{t}}
    • Constant learning rate:
      ηt,i=α \eta_{t,i}=\alpha
    1. 为了保证数据的新鲜性,需要进行在线数据加入,所有的曝光的实例,设置时间t,在时间t内被点击设置为label=1,否则设置label=0,注意时间t不能太大也不能太小,根据现实业务进行设置.
    2. 样本的均匀采样和负样本数据的下采样,由于负样本太多需要对负样本进行下采样。
    3. Model Re-Calibration

    负样本欠采样可以加快训练速度并提升模型性能。但是同样带来了问题:改变了训练数据分布。所以需要进行校准。
    q=pp+(1p)/wq=\frac{p}{p+(1-p)/w}
    其中:

    • w是采样率
    • p是在采样后空间中给出的CTR预估值
    • 计算得到的q就是修正后的结果

    面试十问

    1. lr的权重个数和gbdt的什么有关?

    lr的权重个数,等于gbdt所有叶子节点的个数.

    1. 负样本欠采样之后会对模型有什么影响,怎么解决?

    负样本欠采样可以加快训练速度并提升模型性能。但是同样带来了问题:改变了训练数据分布。所以需要进行校准。
    q=pp+(1p)/wq=\frac{p}{p+(1-p)/w}

    1. GBDT特征的重要性是如何评估的?

    特征j的全局重要度通过特征j在单颗树中的重要度的平均值来衡量:
    J^j2=1Mm=1MJ^j2(Tm)\hat J_j^2=\frac{1}{M}\sum_{m=1}^{M}\hat J_j^2(T_m)
    其中,M是树的数量,特征j在单棵树中的重要度如下:
    J^j2(T)=t=1L1i^j2I(vt=j)\hat J_j^2(T)=\sum_{t=1}^{L-1}\hat i_j^2 I(v_t=j)

    1. gbdt+lr如何训练

    一般是先训练gbdt在训练lr,首先将数据data分成两部分a和b,a用来训练gbdt,b用来训练lr。其中用a训练gbdt的时候,需要将a分成train_a, valid_a, test_a, 得到gbdt之后。将b通过gbdt得到所有对应叶子节点的下标进行one-hot编码.
    继续训练b,将b通过gbdt得到update_b, 将update_b分成训练、验证和测试集,训练得到LR.

    1. 为什么建树采用ensemble决策树?

    一棵树的表达能力很弱,不足以表达多个有区分性的特征组合,多棵树的表达能力更强一些。GBDT每棵树都在学习前面棵树尚存的不足,迭代多少次就会生成多少颗树。按paper以及Kaggle竞赛中的GBDT+LR融合方式,多棵树正好满足LR每条训练样本可以通过GBDT映射成多个特征的需求。

    1. 为什么建树采用GBDT而非RF?

    RF也是多棵树,但从效果上有实践证明不如GBDT。且GBDT前面的树,特征分裂主要体现对多数样本有区分度的特征;后面的树,主要体现的是经过前N颗树,残差仍然较大的少数样本。优先选用在整体上有区分度的特征,再选用针对少数样本有区分度的特征,思路更加合理,这应该也是用GBDT的原因。

    1. GBDT与LR融合方案

    AD ID类特征在CTR预估中是非常重要的特征,直接将AD ID作为feature进行建树不可行,故考虑为每个AD ID建GBDT树。但互联网时代长尾数据现象非常显著,广告也存在长尾现象,为了提升广告整体投放效果,不得不考虑长尾广告。在GBDT建树方案中,对于曝光充分训练样本充足的广告,可以单独建树,发掘对单个广告有区分度的特征,但对于曝光不充分样本不充足的长尾广告,无法单独建树,需要一种方案来解决长尾广告的问题。
    综合考虑方案如下,使用GBDT建两类树,非ID建一类树,ID建一类树。
    1)非ID类树:不以细粒度的ID建树,此类树作为base,即便曝光少的广告、广告主,仍可以通过此类树得到有区分性的特征、特征组合。
    2)ID类树:以细粒度 的ID建一类树,用于发现曝光充分的ID对应有区分性的特征、特征组合。如何根据GBDT建的两类树,对原始特征进行映射?以如下图3为例,当一条样本x进来之后,遍历两类树到叶子节点,得到的特征作为LR的输入。当AD曝光不充分不足以训练树时,其它树恰好作为补充。

    面试真题

    1. 为什么建树采用GBDT而非RF?

    参考

    1. https://blog.csdn.net/u010352603/article/details/80681100
    2. http://www.cbdio.com/BigData/2015-08/27/content_3750170.htm
    3. https://blog.csdn.net/u014297722/article/details/89420421
    展开全文
  • LR算法

    2020-06-11 14:58:40
    LR算法 一、回归分析 回归分析就是利用已知数据样本产生拟合方程,从而对未知数据进行预测。 回归分析算法分类 回归分析算法分为线性回归算法和非线性回归算法。 2.1、线性回归 一元线性回归和多元线性回归。...

    LR算法

    一、回归分析

    回归分析就是利用已知数据样本产生拟合方程,从而对未知数据进行预测。

     

    • 回归分析算法分类

    回归分析算法分为线性回归算法和非线性回归算法。

    2.1、线性回归

    一元线性回归和多元线性回归。

     

    2.2、非线性回归

    一元回归和多元回归。

     

    2.3、广义线性回归

    logistic回归

     

     

    三Logistic Regression算法

    逻辑回归与线性回归本质上是一样的,都是通过误差函数求解最优系数,在形式上只不过在线性回归上增加了一个逻辑函数。

    与线性回归相比,逻辑回归(Logistic Regression,LR )更适用于因变量为二分变量模型,logistic 回归系数可用于估计模型中每个自变量的权重比。

     

    3.1、Sigmoid 函数

    Sigmoid 函数( 海维赛德阶跃函数 ) 在二分类的情况下输出值为(0,1) 之间,可将实数映射到这个区间。

     

     

    可做激活函数 二分类

     

    3.2、正则化

     

     

     

     

    四、GBDT+LR

    正如它的名字一样,GBDT+LR 由两部分组成,其中GBDT用来对训练集提取特征作为新的训练输入数据,LR作为新训练输入数据的分类器。

    4.1、GBDT+LR实现步骤

    1、 GBDT首先对原始训练数据做训练,得到一个二分类器,当然这里也需要利用网格搜索寻找最佳参数组合。

    2、 与通常做法不同的是,当GBDT训练好做预测的时候,输出的并不是最终的二分类概率值,而是要把模型中的每棵树计算得到的预测概率值所属的叶子结点位置记为1,这样,就构造出了新的训练数据。

    举个例子,下图是一个GBDT+LR 模型结构,设GBDT有两个弱分类器,分别以蓝色和红色部分表示,其中蓝色弱分类器的叶子结点个数为3,红色弱分类器的叶子结点个数为2,并且蓝色弱分类器中对0-1 的预测结果落到了第二个叶子结点上,红色弱分类器中对0-1 的预测结果也落到了第二个叶子结点上。那么我们就记蓝色弱分类器的预测结果为[0 1 0],红色弱分类器的预测结果为[0 1],综合起来看,GBDT的输出为这些弱分类器的组合[0 1 0 0 1] ,或者一个稀疏向量(数组)。

     

    这里的思想与One-hot独热编码类似,事实上,在用GBDT构造新的训练数据时,采用的也正是One-hot方法。并且由于每一弱分类器有且只有一个叶子节点输出预测结果,所以在一个具有n个弱分类器、共计m个叶子结点的GBDT中,每一条训练数据都会被转换为1*m维稀疏向量,且有n个元素为1,其余m-n 个元素全为0。

     

    3、 新的训练数据构造完成后,下一步就要与原始的训练数据中的label(输出)数据一并输入到Logistic Regression分类器中进行最终分类器的训练。

    思考一下,在对原始数据进行GBDT提取为新的数据这一操作之后,数据不仅变得稀疏,而且由于弱分类器个数,叶子结点个数的影响,可能会导致新的训练数据特征维度过大的问题,因此,在Logistic Regression这一层中,可使用正则化来减少过拟合的风险,在Facebook的论文中采用的是L1正则化

     

     

    4.2、RF+LR ?  Xgb+LR?对比

      既然GBDT可以做新训练样本的构造,那么其它基于树的模型,例如Random Forest以及Xgboost等是并不是也可以按类似的方式来构造新的训练样本呢?没错,所有这些基于树的模型都可以和Logistic Regression分类器组合。至于效果孰优孰劣,我个人觉得效果都还可以,但是之间没有可比性,因为超参数的不同会对模型评估产生较大的影响。下图是RF+LR、GBT+LR、Xgb、LR、Xgb+LR 模型效果对比图,然而这只能做个参考,因为模型超参数的值的选择这一前提条件都各不相同。

     

     

    顺便来讲,RF也是多棵树,但从效果上有实践证明不如GBDT。且GBDT前面的树,特征分裂主要体现对多数样本有区分度的特征;后面的树,主要体现的是经过前N颗树,残差仍然较大的少数样本。优先选用在整体上有区分度的特征,再选用针对少数样本有区分度的特征,思路更加合理,这应该也是用GBDT的原因。

     

    4.3、GBDT+LR的代码实现解析

    在网上找到了两个版本的GBDT+LR的代码实现,通过阅读分析,认为里面有一些细节还是值得好好学习一番的,所以接下来这一小节会针对代码实现部分做一些总结。

    首先,目前我所了解到的GBDT的实现方式有两种:

    一是利用Scikit-learn中的ensemble.GradientBoostingClassifier

    二是利用lgb里的params={ 'boosting_type': 'gbdt' }参数。接下里分别对这两种实现方式进行分析。

     

    4.3.1Scikit-learn的实现

    from sklearn.preprocessing import OneHotEncoderfrom sklearn.ensemble import GradientBoostingClassifier

     

    gbm1 = GradientBoostingClassifier(n_estimators=50, random_state=10, subsample=0.6, max_depth=7,min_samples_split=900)

     

    gbm1.fit(X_train, Y_train)

    train_new_feature = gbm1.apply(X_train)

    train_new_feature = train_new_feature.reshape(-1, 50)

     

    enc = OneHotEncoder()

     

    enc.fit(train_new_feature)

    # # 每一个属性的最大取值数目

    # print('每一个特征的最大取值数目:', enc.n_values_)

    # print('所有特征的取值数目总和:', enc.n_values_.sum())

    train_new_feature2 = np.array(enc.transform(train_new_feature).toarray())

     

     

    划重点:

    model.apply(X_train)的用法

    model.apply(X_train)返回训练数据X_train在训练好的模型里每棵树中所处的叶子节点的位置(索引)

    OneHotEncoder() 首先fit() 过待转换的数据后,再次transform() 待转换的数据,就可实现对这些数据的所有特征进行One-hot 操作。

    由于transform() 后的数据格式不能直接使用,所以最后需要使用.toarray() 将其转换为我们能够使用的数组结构。

    enc.transform(train_new_feature).toarray()

    sklearn中的GBDT 能够设置树的个数,每棵树最大叶子节点个数等超参数,但不能指定每颗树的叶子节点数。

     

    4.3.2lightgbm 的实现

    params = {

        'task': 'train',

        'boosting_type': 'gbdt',

        'objective': 'binary',

        'metric': {'binary_logloss'},

        'num_leaves': 64,

        'num_trees': 100,

        'learning_rate': 0.01,

        'feature_fraction': 0.9,

        'bagging_fraction': 0.8,

        'bagging_freq': 5,

        'verbose': 0

    }

     

     

    # number of leaves,will be used in feature transformation

    num_leaf = 64

     

    print('Start training...')

    # train

    gbm = lgb.train(params=params,

                    train_set=lgb_train,

                    valid_sets=lgb_train, )

     

     

    print('Start predicting...')

    # y_pred分别落在100棵树上的哪个节点上

    y_pred = gbm.predict(x_train, pred_leaf=True)

    y_pred_prob = gbm.predict(x_train)

     

     

    result = []

    threshold = 0.5

    for pred in y_pred_prob:

        result.append(1 if pred > threshold else 0)

    print('result:', result)

     

     

    print('Writing transformed training data')

    transformed_training_matrix = np.zeros([len(y_pred), len(y_pred[1]) * num_leaf],

                                           dtype=np.int64)  # N * num_tress * num_leafs

    for i in range(0, len(y_pred)):

        # temp表示在每棵树上预测的值所在节点的序号(0,64,128,...,6436 为100棵树的序号,中间的值为对应树的节点序号)

        temp = np.arange(len(y_pred[0])) * num_leaf + np.array(y_pred[i])

        # 构造one-hot 训练数据集

        transformed_training_matrix[i][temp] += 1

     

    y_pred = gbm.predict(x_test, pred_leaf=True)

    print('Writing transformed testing data')

    transformed_testing_matrix = np.zeros([len(y_pred), len(y_pred[1]) * num_leaf], dtype=np.int64)

    for i in range(0, len(y_pred)):

        temp = np.arange(len(y_pred[0])) * num_leaf + np.array(y_pred[i])

        # 构造one-hot 测试数据集

        transformed_testing_matrix[i][temp] += 1

     

    划重点

    params 字典里超参数的设置,因为是二分类问题,所以设置 {'boosting_type': 'gbdt','objective': 'binary','metric': {'binary_logloss'}},然后设置树的个数及每棵树的叶子结点个数{'num_leaves': 64,'num_trees': 100}

     model.predict(x_train, pred_leaf=True)使用

    返回训练数据在训练好的模型里预测结果所在的每棵树中叶子节点的位置(索引),形式为7999*100的二维数组。

     构造Ont-hot数组作为新的训练数据

    这里并没有使用sklearn中的OneHotEncoder(),也没有使用pandas中的get_dummies(),而是手工创建一个One-hot数组。(当然也可以像5.1.2 那样操作)

     

    首先,创建一个二维零数组用于存放one-hot的元素;

    然后,获取第2步得到的二维数组里每个叶子节点在整个GBDT模型里的索引号,因为一共有100棵树,每棵树有64个叶子节点,所以索引范围是0~6400;(这里有一个技巧,通过把每棵树的起点索引组成一个列表,再加上由落在每棵树叶子节点的索引组成的列表,就得到了往二维零数组里插入元素的索引信息)

    最后,

    temp = np.arange(len(y_pred[0])) * num_leaf + np.array(y_pred[i])

    5.2.4 对二维数组填充信息,采用"+=" 的方法

    # 构造one-hot 训练数据集

    transformed_training_matrix[i][temp] += 1

     

    6. GBDT + LR 模型提升

    现在,我们思考这样一个问题,Logistic Regression是一个线性分类器,也就是说会忽略掉特征与特征之间的关联信息,那么是否可以采用构建新的交叉特征这一特征组合方式从而提高模型的效果?

    其次,我们已经了解到GBDT很有可能构造出的新训练数据是高维的稀疏矩阵,而Logistic Regression使用高维稀疏矩阵进行训练,会直接导致计算量过大,特征权值更新缓慢的问题。

    针对上面可能出现的问题:

    FM算法解析及Python实现 ,使用FM算法代替LR,这样就解决了Logistic Regression的模型表达效果及高维稀疏矩阵的训练开销较大的问题。

    然而,这样就意味着可以高枕无忧了吗?当然不是,因为采用FM对本来已经是高维稀疏矩阵做完特征交叉后,新的特征维度会更加多,并且由于元素非0即1,新的特征数据可能也会更加稀疏,那么怎么办?所以,我们需要再次回到GBDT构造新训练数据这里。当GBDT构造完新的训练样本后,我们要做的是对每一个特征做与输出之间的特征重要度评估并筛选出重要程度较高的部分特征,这样,GBDT构造的高维的稀疏矩阵就会减少一部分特征,也就是说得到的稀疏矩阵不再那么高维了。之后,对这些筛选后得到的重要度较高的特征再做FM算法构造交叉项,进而引入非线性特征,继而完成最终分类器的训练数据的构造及模型的训练。

     

     

     

    展开全文
  • LR 类分析方法总结

    千次阅读 2019-04-25 18:41:33
    文章目录LR 类分析方法相关定义LR 类分析法的工作过程LR 分析表LR 驱动程序LR(0) 分析法LR(0) 分析法基本概念LR(0) 项目项目集的闭包项目集的投影项目集的转换函数(GO 函数)构造 LR(0) 可归前缀状态机 LRSMLR(0) ...

    LR 类分析方法相关定义

    LR(k) 分析方法是 1965 年由 D. Knuth 提出来的一种自底向上的语法分析文法。这里的 L 表示从左向右扫描输入串,R 表示构造一个最右推导的逆过程,k 表示在决定语法分析动作时需要向前看的符号个数。由于 LR(k) 分析方法对文法的限制很少,因而大多数能用上下文无关文法描述的程序设计语言都可用 LR 分析法进行有效的分析。LR(k) 分析方法适用范围较广、分析速度很快,并且能够准确及时地发现语法错误,因此,LR 分析法是当前最一般的语法分析方法(这里我书上是这样写的,但是我听老师好像说过因为移入归约冲突等不好解决就用的少了)。LR 分析法主要有 LR(0)、SLR(1)、LR(1)、LALR(1) 方法。

    下面是其中的一些术语以及定义:

    • 规范句型: 用最右推导导出的句型,也称右句型。
    • 规范前缀: 若有规范句型 αβ, 且 β 是终极符串或空串, 则称 α 为规范前缀。
    • 规范活前缀:若某一规范句型的规范前缀 α 不含句柄或含一个句柄并且具有形式 α=α′π( π 是句柄),则称规范前缀 α 为规范活前缀(简称活前缀)。
    • 可归前缀:若活前缀 α 是含句柄的规范前缀,且句柄在 α 的最右端,即有 α=α′π,且 π 是句柄,则称活前缀 α 为可归前缀(归约规范活前缀、归约活前缀和可归活前缀都是指这个)

    注:可归前缀中,『句柄在 α 的最右端』这里说的『最右端』是因为规范句型是指最右推导导出的句型,因此每次都选择最右边的非终极符进行替换。如果最右边出现了句柄,则可以将句柄归约。(这里存疑,看本节例题就可以很容易从符号栈中看出是从最右端开始的,因为例题中符号栈栈底在左边)

    LR 类分析法的基本思想和工作过程

    img

    LR 方法的主要思想是,从输入流依此把符号移入符号栈,直至栈顶出现一个句柄;之后对句柄进行归约,直至栈顶不出现句柄;重复上述过程,直至最终归约为一个开始符,且输入流为空。

    一个 LR 分析器由以下几个部分组成:

    1. 输入流(Input):一个有待分析的输入符号串
    2. 分析栈:其中包括文法符号栈和相应的状态栈
    3. 分析表:其中包括 Action 表和 GoTo 表。
    4. 驱动程序:对于不同的 LR 分析方法其驱动程序都是相同的。

    LR 分析表

    LR 分析表是提取可归前缀状态机 LRSM 中的信息形成的矩阵形式的表。包括 Action 表和 GoTo 表两部分:

    • Action 矩阵:行代表状态,列代表输入符(包括输入结束符 #,值得注意的是输入符都是终极符),而矩阵元素则表示相应的分析动作:移入 Shift / 归约 Reduce / 成功 Accept / 失败 Error

    • GoTo 矩阵:行代表状态,列则代表非终极符,而矩阵元素则表示归约后的转向状态。

    • Action(s, a) 规定了当状态 s 面对输入符号 a 时采用什么动作,用『动作矩阵』存储

    • Goto(s, x) 规定了状态 s 面对符号 x 时下一个状态是什么,用『转向矩阵』存储。

    LR 驱动程序

    不妨假设状态栈、符号栈和输入流的开始格局为:(#S1, #, a1a2…an#),各部分以逗号分隔。

    • 移入:若当前格局为(#S1S2…Sn, #X1X2…Xn, aiai+1…an#),且 Action(Sn, ai)=Sj,ai∈VT,则 ai 入符号栈,第 j 个状态 Sj 入状态栈。即移入后的格局变为:(#S1S2…Sn Sj, #X1X2…Xn ai, ai+1…an#)
    • 归约:若当前格局为(#S1S2…Sn, #X1X2…Xn, aiai+1…an#),且 Action(ISn, a)=Rj,a∈VT∪{#},则按照第 j 个产生式进行归约,符号栈和状态栈相应元素退栈,归约后的文法符号入栈。假设第 j 个产生式为 A→α,k=|α| (α=Xn-k+1…Xn),则归约后的格局变为:(#S1S2…Sn-kS, #X1X2…Xn-kA, aiai+1…an#),其中 S=GoTo(Sn-k, A)。
    • 成功:若状态栈的栈顶状态为 Si,输入流当前值为 #,且 Action(Si, #)=Accept,则分析成功。
    • 失败:若状态栈的栈顶状态为 Si,输入流当前值为 a,且 Action(Si, a)=Error 或空,则转向出错处理程序。

    例: 设文法 G(S) : \
    [1] S → aAdBc \
    [2] A → Aaa\
    [3] A → a\
    [4] B → Bbb\
    [5] B → b

    则对句子 aaaacbbbd 有如下规范推导:

    S ⇒ aAdBc[1] \
    ⇒ aAdBbb[4]c[1] \
    ⇒ aAdb[5]bb[4]c[1] \
    ⇒ aAaa[2]db[5]bb[4]c[1] \
    ⇒ aa[3]aa[2]db[5]bb[4]c[1]

    其归约过程为:

    aa[3]aa[2]db[5]bb[4]c[1] \
    ⇒ aAaa[2]db[5]bb[4]c[1] \
    ⇒ aAdb[5]bb[4]c[1] \
    ⇒ aAdBbb[4]c[1] \
    ⇒ aAdBc[1] \
    ⇒ S

    LR 分析法的关键问题

    • 如何判断分析栈内容是否为可归前缀
    • 能唯一的确定可归前缀中的句柄

    可归前缀的判断

    我们有如下『派生定理』

    1. 开始符产生式的右部是可归前缀。
    2. 如果 α1Aα2 是可归前缀,且 A→β 是产生式,则 α1β 也是可归前缀。

    第一个就是 Z->α1|α2|...αn 则 α1,…αn 都为可归前缀,这个结论对不对稍微分析一下就可以知道。

    比如说一开始符号栈里是 α1,输入流是一个#,相当于 β=空,所以 α1 首先是一个规范前缀,然后他也是必然为一个句柄,也就是可归前缀了。所以第一点就是开始符的右部都是可归前缀。以它为基础可以推出其他的可归前缀。

    第二个结论怎么得出来的呢,思想是 α1Aα2 是可归前缀,这里的右端肯定有句柄,大概有几种可能

    • 要么 A 不在句柄里,而在 α2 里,假如 A 不在里面的话,α2 有一颗子树,又有 A→β,α1 里面一定没有句柄存在,否则 α1Aα2 是可归前缀就不可满足。这样的话,α1β 一定是含有句柄,并且在最右边。
    • 要么 A 在句柄里,假如 A 在句柄里,一定是一层节点,然后 A→β 是下一层的,所以依然成立。

    例子:设有文法 G[S]:

    [1] S → aAc
    [2] A → Abb
    [3] A → b

    其中可归前缀为:aAc,aAbb,ab

    确定句柄

    因为本文是先画可归前缀状态机的,然后进行 Action 矩阵等的填写。而可归前缀状态机本身就可以提供出可归前缀的判断信息(待补充如何判断)

    LR(0) 分析法

    LR(0) 分析法基本概念

    LR(0) 项目

    LR(0)项目(简称项目)是带一个圆点 『•』 的产生式。每个项目都标志分析时的某一状态。

    即:若 A→α 是产生式,则称 A→α•β 为 LR(0) 项目(简称项目)

    项目的含义:对产生式 A 进行分析的某个状态:圆点的左部表示分析过程已经识别过的部分,圆点右部表示期待的后缀部分。简单来说就表示分析到哪了。

    • 形如 A→ α• 的项目称为 归约型项目 (点在最后)
    • 形如 A→ α•β 的项目称为 移入型项目

    这里有个特殊情况是:•ε 这一个是看作归约项的。

    归约型项目按照自动机来说,实际上是自动机的一个终止状态,也就自动机可以接受的一个 可归前缀

    例:起始符 S → aAc 对应四个 LR(0) 项目:

    1. S → •aAc 表明我们希望接下来在输入中看到一个从 aAc 推导得到的串。这时符号栈为空。
    2. S → a•Ac 表明刚在输入中看到了一个 a,我们希望接下来看到一个能从 Ac 推导得到的串。这时符号栈中包含活前缀 a。
    3. S → aA•c 表明刚在输入中看到了一个可以由 aA 推导得到的串。这时符号栈中包含活前缀 aA。
    4. S → aAc• 表明我们已经看到了产生式全体 aAc,已经是时候把 aAc 归约为 S 了。符号栈中为可归前缀,右部为句柄。

    项目集的闭包

    项目集就是项目的集合的简称。

    假设 IS 是 LR(0) 项目集,则称下面 CLOSURE(IS) 为 IS 的闭包集:

    CLOSURE(IS) = IS∪{ A→•π | Y→β•Aη∈CLOSURE(IS),A→π是产生式 }

    项目集的闭包 CLOSURE(IS) 的含义:表示项目集 IS 中每一个 Y→β•Aη 项目的圆点右部非终极符 A,可以应用的所有可能产生式。

    值得注意的是,这是一个动态的过程。也就是说,A 可以应用的产生式作为项目后,还要继续判断这些新产生的项目。

    例 1:G[S]:

    • [1] S → aAc (S 可以应用的产生式)
    • [2] A → dbb (A 可以应用的产生式)
    • [3] A → b (A 可以应用的产生式)

    令项目集 IS={S → a•Ac[1]},则

    CLOSURE(IS) ={S→a•Ac[1],A→•dbb[2],A→•b[3] }

    例 2:G[S]:

    • S → E
    • E → E+T | T
    • T → T*F | F
    • F → (E) | id

    令项目集 IS={S→•E}

    CLOSURE(IS):

    • S → •E
    • E → •E+T
    • E → •T
    • T → •T*F
    • T → •F
    • F → •(E)
    • F → •id

    项目集的投影

    假设 IS 是 LR(0) 项目集,则称下面定义的 IS(X) 为 IS 关于 X 的投影集:

    IS(X) = {A→αX•β | A→α•Xβ∈IS, X∈(VT∪VN)}

    项目集 IS 关于 X 的投影集的含义:项目集中每个项目 A→α•Xβ 所描述的状态,处理完一个符号 X 后所对应的后继项目。

    这里说句人话就是 IS 中的一个项目接收一个符号(终极符或非终极符)到达的下一个项目。

    比如 S → •aAB 这个项目可以接收 a 会到 S → a•AB 这个项目。不能接收 A 所以到空状态。

    这样对项目集里的所有项目尝试接收所有终极符和非终极符后能到的下一个项目就是该项目关于那个满足条件的符号的投影集。

    例:G[S]:

    • [1] S → aAB
    • [2] A → dbb
    • [3] A → b
    • [4] B → e

    令项目集 IS={S → a•AB[1],A → •dbb[2] ,A →•b[3] },则:

    • IS(A)={S → aA•B[1]}
    • IS(S)={ }
    • IS(d)={A →d•bb [2] }
    • IS(b)={A →b•[3] }
    • IS(a)={ }
    • IS©={ }

    项目集的转换函数(GO 函数)

    若 IS 是一个 LR(0) 项目集,X∈(VT∪VN),函数 GO(IS, X) 定义为

    GO(IS, X)=CLOSURE(IS(X))

    其中 IS(X) 为 LR(0) 项目集 IS 关于 X 的投影。

    例:G[S]:

    • [1] S → aAc
    • [2] A → dbb
    • [3] A → b

    令项目集 IS={S → •aAc [1]},则:

    GO(IS, a )
    = CLOSURE(IS(a))
    = CLOSURE({S→a•Ac[1]})
    = {S → a•Ac[1],A → •dbb[2],A → •b[3]}

    构造 LR(0) 可归前缀状态机 LRSM

    这里的 LRSM 应该是 线性正则式状态机,即 linear regex state machine,这个名词我并没有 Google 到,貌似是我们学校老师起得名字。

    不过可能也是 LR state machine,反正我也不太知道。

    为了使“成功”状态易于识别,通常 LR 文法要求文法的开始符 唯一 且不出现于产生式的右部,因此要增加一个新的产生式,称 拓广产生式

    Z→S

    其中 S 是原文法的开始符,而 Z 则是新符号。

    下面给出构造 LR(0) 可归前缀 LRSM 的算法

    • Step1. 构造初始状态 IS0 :IS0=CLOSURE({Z→•S}),其中 Z 是开始符
    • Step2. 已构造的可归前缀自动机的任一状态 IS,对每个符号 X∈VT∪VN,通过项目集转移函数 GoTo(IS,X) 求其后继状态 ISj
    • Step3. 重复 Step2,直至所有状态处理完毕。

    img

    LRSM 特点:

    • LRSM 给出了所有的可归活前缀
    • LRSM 中的每个状态将对应一个项目集:
      1. 其中一部分是由先驱状态分出来(称为基本项目);
      2. 一部分则是由基本项目扩展出来的(称为扩展项目或派生项目)。派生部分项目的特点是其中的“•”出现在产生式右部的最左侧。
    • LRSM 不能直接用于 LR 分析,因为它识别的是 VT ∈ VN上的符号串,而语法识别的是 VT组成的句子。但它提供了 LR 分析所需的信息。
    • LRSM 提供的信息:
      • 移入/归约信息: [A→α•a β]、[A→π•];
      • 移入/归约后的转向状态信息.

    可归前缀状态机实际上不能够完成语法分析的全部工作,他只能用来找句柄(走到某一个终止状态,我们就知道出现了可归前缀,句柄是什么也知道了),除此之外我们还需要做其他的工作来完成语法分析。

    首先我们来看可归前缀状态机提供的语法分析信息:

    1. 合法性检查信息,就是当前允许输入哪些符号
    2. 移入/归约信息,移入状态可以做移入动作,归约状态可以做归约动作
    3. 移入/归约后的转向信息,移入简单这里不解释,归约就回归到含有 Vn 输出边的状态,然后按照 Vn 进行移入。

    什么是移入和归约信息呢?

    1. 移入信息:如果可归前缀状态机 ISi 包含形如 A→α•aβ 的项目,即 ISi 有 a 的输出边,其中 a 是终极符,则表示 ISi 状态遇当前输入符为 a 时应将其移入符号栈,状态机沿着其 a 的输出边转向其后继状态。
    2. 归约信息:如果状态 ISi 包含形如 X →Y1 Y2 ……Yn• 的项目,则表示 ISi 状态可按该产生式做归约动作,归约后状态机回退 n 个(Y1 Y2 ……Yn 的长度)状态至状态 ISj,随后沿着 ISj 的 X 输出边转向其后继状态。

    例子:
    构造 LR(0)状态机

    • S → E $
    • E → E + T
    • E → T
    • T → id
    • T → ( E )

    img

    对图像做一些解释:

    • 黄色的部分是状态的编号
    • 绿色的部分是求闭包产生的
    • 四个 0 是 •,因为编码原因,不能正常显示。

    上面那个图是从状态 0 开始画得,然后编号这个基本都是随便编的,每个人的不需要一样。

    本例不是太好,因为状态 0 包含了全部的产生式,可能会让人误解一开始就要包含所有的产生式,其实这里一开始只有 S → •E$ 这一个项目的。这里需要注意的是:其余的都是这个项目的闭包。这样计算完后才导致文法的所有产生式都在里面。

    LR(0) 分析表的构造

    假设 ISk 为 LR(0) 项目集,

    • Action 矩阵:
      1. 若 A→α•aβ∈ISk,且 GO(ISk, a)= ISi,a∈VT,则 Action(ISk, a)=Si,表示移入动作。
      2. 若 A→α•∈ISk,则对任意 a∈VT∪{#},令 Action(ISk, a)=Rj,其中产生式 A→α 的编号为 j,表示用编号为 j 的产生式进行归约。
      3. 若 Z→α•∈ISk,且 Z 为拓广产生式的左部非终极符(文法的开始符),则 Action(ISk, #)=Accept。
      4. 其它情形,则 Error(n),表示出错标志,也可不填。
    • GoTo 矩阵:
      • 若 GO(ISk, A)=ISi,A∈VN,则 GoTo(ISk, A)=i。

    例子

    1. S → E $
    2. E → E + T
    3. E → T
    4. T → id
    5. T → ( E )
    Action 表 GoTo 表
    $ + i ( ) # E T
    0 S5 S6 1 9
    1 S2 S3
    2 Ac
    3 S5 S6 4
    4 R2 R2 R2 R2 R2 R2
    5 R4 R4 R4 R4 R4 R4
    6 S5 S6 7 9
    7 S3 S8
    8 R5 R5 R5 R5 R5 R4
    9 R3 R3 R3 R3 R3 R3

    解释

    • 这里 S 打头的都是移入,R 打头的都是归约
    • 对于归约项目 4 5 8 9 来说,它们 R 后面的是对应产生式的序号,表示使用第几个产生式进行归约。
    • 对于移入项目来说,S 后面的接收终极符后到达的项目状态的序号

    LR(0) 驱动程序

    见开头

    LR(0) 分析实例

    G[S]

    • S → E$ [1]
    • E → E+T [2] | T [3]
    • T → id [4] | (E) [5]

    分析:i+i$

    状态栈 符号栈(#代表栈底) 输入串(#代表输入串结束) Action Goto
    0 # i+i$# A[0,i]=S5
    05 # i +i$# reduce4 G[0,T]=9
    09 # T +i$# reduce3 G[0,E]=1
    01 # E +i$# A[1,+]=S3
    013 # E+ i$# A[3,i]=S5
    0135 # E+i $# reduce4 G[3,T]=4
    0134 # E+T $# reduce2 G[0,E]=1
    01 # E $# A[1,$]=S2
    012 # E$ # accept

    这里没啥好说的,对应着 驱动程序 小节那里写就行了。

    还是稍微解释一下吧,两个月后自己也看不太明白了…

    刚开始时在状态 0,读入输入串头符 i,查 Action 表可得转到状态 5,把状态 5 压入状态栈中,也把输入串头符 i 压入符号栈中。

    然后在状态 5 读入输入串头符,查 Action 表是按第四条产生式 T → id 进行归约,即从符号栈栈顶取 i 归约成非终极符 T。现在就相当于状态 5 接收一个归约产生的 T,查 Goto 表可得由状态 5 转到状态 9。

    接着这样做就行,需要注意的是,我描述的部分并没有详细的说明符号栈、输入串的变化情况,要想详细知道规则,看 驱动程序 小节。

    LR(0) 文法的限定条件 | 定义

    • 若 LRSM 中存在一个状态(项目集)
      • 既包含移入型项目又包含归约型项目,则说有 移入-归约冲突
      • 如果同时存在两个或两个以上归约型项目,则说有 归约-归约冲突
    • 若 LRSM 中任何状态都不存在冲突,则称该文法为 LR(0) 文法。

    SLR(1) 分析法

    LR(0) 分析法的不足

    • LR(0) 方法对文法的要求严格。
    • LR(0) 方法容易出现冲突状态。

    不是 LR(0) 文法的情况

    我们先来看一个例子:

    img

    3 状态为二义性节点,有移入-归约冲突,原因是没有充分的利用输入流信息

    我们来分析一下这个例子,对于状态 3:如果输入流头符为 ‘*’ 就应该移入;如果头符属于 Follow(T) 则应该归约。因此结论就是如果 *∉follow(T) 则这个冲突其实是可以解决的。

    SLR(1) 的可归状态机可以直接使用 LR(0) 的状态集,只是出现冲突的时候,向前看一个符号来消除冲突。

    总结如下:

    1. 如果某个状态有项目集:{ A→α•, D→μ•dγ},则存在 移入-归约冲突。可以如下解决:
    • 若当前输入符在 A 的 Follow 集中,则应用 A →α• 归约;
    • 若当前输入符为 d 则应移入 。
    • 而对当前输入符为 d,d 又在 A 的 Follow 集中,则无法解决。
    1. 如果某个状态有项目集:{ A→α•, B→β•},则存在着 归约-归约冲突 。可以如下解决:
    • 若用 A→α• 归约,则当前输入符应在 A 的 Follow 集中
    • 若用 B→β• 归约,则当前输入符应在 B 的 Follow 集
    • 当前输入符应在 A 的 Follow 集中又在 B 的 Follow 集中,则无法解决。
    1. 如果某个状态有如下项目集:{ A→α•, B→β•, D→μ•dγ },则存在着归约-归约,移入-归约冲突
    • 若用 A→α• 归约,则当前输入符应在 A 的 Follow 集中
    • 若用 B→β• 归约,则当前输入符应在 B 的 Follow 集
    • 若移入,则当前输入符应为 d。

    SLR(1) 分析条件

    LRSM0 中存在着状态:
    { A1→α1•,
    …,
    An→αn•,
    B1→β1•a1r1
    …,
    Bm→βm•amrm }
    则集合:
    Follow(A1)、…、Follow(An)、{a1, …, am} 两两相交为空时才能使用 SLR(1) 方法分析。

    注:a1, …, am 中可以有相同者。

    构造 SLR(1) 可归前缀状态机

    这里和 LR(0) 一样

    SLR(1) 分析表的构造

    与 LR(0) 分析表的构造不同之处只是 Action 表中归约项的填写,其他都一样。

    • 若 A→α•∈ISk,则对任意 a∈VTa∈Follow(A),令 Action(ISk, a)=Rj,其中产生式 A→α的编号为 j,表示用编号为 j 的产生式进行归约。

    构建 SLR(1) 分析表和 LR(0) 分析表的区别就在归约项这里,原来是对于任意一个 a,现在是对于特殊的 a。

    完整方法如下:

    假设 ISk 为 LR(0) 项目集,

    • Action 矩阵:
      1. 若 A→α•aβ∈ISk,且 GO(ISk, a)= ISi,a∈VT,则 Action(ISk, a)=Si,表示移入动作。
      2. 若 A→α•∈ISk,则 对任意 a∈VT,a∈Follow(A),令 Action(ISk, a)=Rj,其中产生式 A→α的编号为 j,表示用编号为 j 的产生式进行归约。
      3. 若 Z→α•∈ISk,且 Z 为拓广产生式的左部非终极符(文法的开始符),则 Action(ISk, #)=Accept。
      4. 其它情形,则 Error(n),表示出错标志,也可不填。
    • GoTo 矩阵:
      • 若 GO(ISk, A)=ISi,A∈VN,则 GoTo(ISk, A)=i。

    例子:

    1. M → T
    2. T → F
    3. T → F*T
    4. F → a

    首先画出该例的可归前缀状态图

    img

    算出各非终极符的 Follow 集

    • Follow(M)={#}
    • Follow(T)={#}
    • Follow(F)={*,#}
    Action 表 GoTo 表
    a * # T F
    0 S2 1 3
    1 Ac
    2 R4 R4
    3 S4 R2
    4 S2 5 3
    5 R3

    原来状态 1 2 3 5 下的 Action 表都会填满,现在只填属于非终极符的 Follow 集的。

    SLR(1) 驱动程序

    见开头

    SLR(1) 分析实例

    a*a*a

    状态栈 符号栈 输入串 分析动作 转向状态
    0 # a*a*a# S2
    02 #a *a*a# R4 3
    03 #F *a*a# S4
    034 #F* a*a# S2
    0342 #F*a *a# R4 3
    0343 #F*F *a# S4
    03434 #F*F* a# S2
    034342 #F*F*a # R4 3
    034343 #F*F*F # R2 5
    034345 #F*F*T # R3 5
    0345 #F*T # R3 5
    01 #T # AC

    SLR(1) 文法限定条件 | 定义

    语法分析表单值

    对于一个文法,若按照上述算法构造的分析表中没有冲突动作,则称该文法为 SLR(1) 文法。

    例子
    有如下文法 G[T]:

    1. T→F*T
    2. T→F
    3. F→a
      该文法是不是 LR(0) 文法,是不是 SLR(1) 文法 .

    拓广产生式后的文法:

    1. Z → T
    2. T→F*T
    3. T→F
    4. F→a

    文法 G[T]的 LR(0)可归前缀状态机

    img

    因状态 2 存在移入-归约冲突,所以该文法不是 LR(0) 文法 .

    但因:Follow(T)={#}、*∉Follow(T),所以该文法为 SLR(1) 文法 .

    SLR(1) 与 LR(0)

    • SLR(1) 和 LR(0) 具有相同的状态机和驱动程序,只是分析表 Action 矩阵在归约条件时有差别。
    • LR(0) 归约时只看分析栈的内容 , 不考虑当前输入符,SLR(1) 在归约动作有冲突时考虑输入符,用 follow 集来解决冲突,因此 SLR(1) 要比 LR(0) 分析能力强。
    • 从定义可以看出 SLR(1) 分析方法是用 LR(0) 项目构成的 LRSM0 来识别活前缀,因此它们的状态数相同,但是,由于 LR(0) 方法只看状态栈的内容而 SLR(1) 方法还要向前看展望符,因此 SLR(1) 文法要比 LR(0) 文法应用广。

    LR(1) 分析法

    LR 文法都是构造性定义,就是说没法直接判断是哪种,都是要画出状态图以后再判断,给我们个文法我们也不知道用 LR 或者说哪种 LR 方法来算,必须是画图后确定。

    他们能解决的问题都是语法分析中的一类问题,并不是所有的问题都能解决。比如归约-归约冲突,我们能解决的是当 follow(a)∩follow(b) 等于空时才能解决,交集不等于空的时候,我们还是不知道怎么办

    移入-归约冲突的时候也有不能解决的时候,用 SLR 还是解决不了

    有人给出了 LR(1) 对输入串进行更加细化的分析

    SLR(1) 分析法的不足

    有如下文法

    • Z→B1aB2bB3c
    • B→d

    SLR(1) 归约时向前看一个符号,但是不区分相同语法符号的不同出现。上述文法中,B 出现了三次,很显然 B1 的后继符只能是 a,B2 的后继符只能是 b,B3 的后继符只能是 c。而 Follow(B)={a,b,c},这表示分析到 B1 时,SLR(1) 方法判断输入串头符可以是 abc,但实际上输入串头符只能是 a,所以此时用 SLR(1) 就失去了精度。

    因此在这个基础上,我们引出了 LR(1) 的方法,多考虑一些信息,多看一个展望符。

    几种 LR 方法的简单对比

    • LR(0) 方法不依赖输入流,直接判定归约,容易出现冲突。
    • SLR(1) 方法简单的把非终极符的 Follow 集做为可归约的依据,并不精确。
    • 一个非终极符在不同的位置不同状态上出现,它所允许的后继符是不同的。LR(1) 针对产生式的非终极符在不同位置上,分别定义其归约后继符集(展望符集 Reducelookup),减少了移入-归约冲突、归约-归约冲突。

    后继符(搜索符)的概念:

    • 不同的归约中有不同的后继符。
    • 特定位置的后继符是 FOLLOW 集的子集

    LR(1) 的基本概念

    LR(1) 基本思想

    构造各种 LR 分析器的任务就是构造其 Action 表和 GoTo 表,其他部分基本相同。

    LR(1) 的基本思想是对非终极符的每个不同出现求其后继符,而不是给每个非终极符求其统一的后继符,我们称其为 展望符集

    LR(1) 项目、投影

    LR(1) 项目:[A→α•β, a],即 LR(0) 项目及一个 VT∪{#} 的展望符组成的二元组。

    用 IS 表示 LR(1) 项目的集合,简称 LR(1) 项目集。其中,项 Z→•α 的展望符为 #,Z 为开始符。

    IS(X):LR(1) 项目集 IS 对于 X 的投影

    IS(X) = { [A→αX•β, a] | [A→α•Xβ, a]∈IS }

    LR(1) 项目有个展望符,当是移入项目的时候展望符是没有作用的。要归约的时候,比如是 a->a•b 这种的时候有作用 ,表示当输入流的头符是符号 b 的时候才进行归约,否则移入。

    项目不同了,投影跟以前一样

    LR(1) 闭包集、GO 函数

    闭包集:

    CLOSURE(IS) = IS∪{[A→•β, a] | [B→α1•Aα2, b]∈CLOSURE(IS),

    其中 A→β 是产生式,a∈First(α2b)}

    这里写完自己看了一会才搞懂,解释一下:

    • 我们先有 [B→α1•Aα2, b] 这个项目
    • 点•后面是个非终极符,所以我们要对它求闭包
    • 以这个非终极符开头的产生式假如有 A→β
    • 则该非终极符产生的 LR(0) 项目是 A→•β ,这个项目的展望符 a 满足 a∈First(α2b)}

    GO:若 IS 是一个 LR(1) 项目集,X 是一个文法符号 , 则

    GO(IS,X)=CLOSURE(IS(X)) GO(IS,X)=CLOSURE(IS_{(X)})

    构造 LR(1) 可归前缀状态机 LRSM1

    • Step1. 构造初始状态 IS0:IS0 = CLOSURE({ [Z→•S, #] })。
    • Step2. 从已构造的 LRSM1 部分图选择未处理的任一状态 IS,对每个符号 X∈VT∪VN,求其后继状态 ISj = CLOSURE(IS(X)),同时在 IS 和 ISj 之间画有向 X 边。
    • 重复 Step2,直至所有状态结点处理完为止。

    例子:构造下列文法的 LR(1) 状态机。

    1. Z → S
    2. S → AaAb
    3. S → BbBa
    4. A → ε
    5. B → ε

    img

    这里就是展望符求法是个重点,总结如下:

    • 开始符的展望符是 #
    • 其他项目的展望符在求闭包时产生,公式在 LR(1) 闭包集、GO 函数 小节。

    LR(1) 分析表的构造

    LR(1) 的项目分为两部分:[A→α•β, a ],即 LR(0) 项目部分(项目的心)和一个终极符的展望集。展望符集{a}是在项目的心分析完,归约到 A 后,当前输入可能出现的终极符集。{a} 在整个项目的投影分析过程都不会改变。

    所以,对 LR(1) 分析表的构造和 LR(0) 分析表大部分相同,只是在归约动作 Ri 的矩阵元素的确定上,需要通过展望符集中的字符来确定列项。

    LR(1) 驱动程序

    见开头

    LR(1) 分析实例

    例 1

    有文法:

    1. Z → BB
    2. B → aB
    3. B → b

    img

    Action 表 GoTo 表
    a b # B
    0 S6 S4 1
    1 S5 S3 2
    2 AC
    3 R3
    4 R3 R3
    5 S5 S3 8
    6 S6 S4 7
    7 R2 R2
    8 R2
    状态栈 符号栈 输入串 Action GoTo
    0 abaab# S6
    0,6 #a baab# S4
    0,6,4 #ab aab# R3 7
    0,6,7 #aB aab# R2 1
    0,1 #B aab# S5
    0,1,5 #Ba ab# S5
    0,1,5,5 #Baa b# S3
    0,1,5,5,3 #Baab # R3 8
    0,1,5,5,8 #BaaB # R2 8
    0,1,5,8 #BaB # R2 2
    0,1,2 #BB # AC

    例 2

    设文法 G[S] 为:

    1. S→AS | ε
    2. A→aA | b
    • 证明 G[S] 是 LR(1) 文法;
    • 构造它的 LR(1) 分析表;
    • 给出符号串 abab# 的分析过程

    G[Z]:

    [0] Z → S

    [1] S → AS

    [2] S → ε

    [3] A → aA

    [4] A → b

    img

    Action

    a b #
    0 S3 S4 R2
    1 Acc
    2 S3 S4 R2
    3 S3 S4
    4 R4 R4 R4
    5 R1
    6 R3 R3 R3

    GoTo

    A S
    0 2 1
    1
    2 2 5
    3 6
    4
    5
    6
    状态栈 符号栈 输入流 动作
    0 # abab# 移入 S3
    03 #a bab # 移入 S4
    034 #ab ab # 归约 R4 转 S6
    036 #aA ab # 归约 R3 转 S2
    02 #A ab # 移入 S3
    023 #Aa b# 移入 S4
    0234 #Aab # 归约 R4 转 S6
    0236 #AaA # 归约 R3 转 S2
    022 #AA # 归约 R2 转 S5
    0225 #AAS # 归约 R1 转 S5
    025 #AS # 归约 R1 转 S1
    01 #S # 成功

    LR(1) 分析条件 | 定义

    LR1 的活前缀状态机中存在着状态:

    A1→α1•, {a11,a12,…}

    …,

    An→αn•, {an1,an2,…}

    B1→β1•c1r1, {b11,b12,…}

    …,

    Bm→βm•cmrm ,{bm1,bm2,…}

    则集合 {a11,a12,…} 、…、{an1,an2,…}、{c1, …, cm} 两两相交为空。

    注: {an1,an2,…} 为 A1 项的展望符集。
    c1, …, cm 为可移入终极符,可以有相同者。

    对于一个文法,若按照上述算法构造的分析表中没有冲突动作,则称该文法为 LR(1)文法。

    LALR(1) 分析法

    LR(1) 语法分析方法的不足

    LR(1) 语法分析方法对展望符的计算比较精确,适应的文法类较广。但 LR(1) 状态机的状态数太多,其构造分析表的工作量及所占的存储空间较大,使其使用和推广受到限制。

    解决方法:

    • 合并 LR(1) 状态机中的等价状态
    • 在 LR(0) 状态机中用传播方式求每个项目集的展望符

    LALR(1) 的思想来源

    LR(1) 的最主要问题是 状态用的太多,以至于有些大语言难以在某些微机上实现。因此,必须给出功能较强且状态数不多的切实可行的方法。

    LR(1) 状态机中扩展项集 A 的闭包生成与 LR(0) 是一样的,只是增加了该状态下对 B 的展望符集 b∈First(βa) 部分,以增加对 B 分析完成后 (B→γ•) 是否进行归约的精准判断。

    所以 LR(1) 状态机中会由于展望符的不同,使得同一核心项目集被分裂多个不同状态,造成状态数目的剧烈增加,导致时间和空间上的急剧上升;

    在 LR(1) 状态机出现很多同心状态,而 LALR(1) 状态机则考虑将 LR(1) 中的同心状态进行合并,从而大大减少状态数,这就是 LALR(1) 和 LR(1) 的主要差别。

    同心状态

    • 项目的心:假设 [A→α•β, b] 是 LR(1) 项目,则称其中的 LR(0) 项目部分 A→α•β 为该项目的心。
    • 状态的心:设 S 是 LR(1) 状态机的一个状态,则 S 的所有项目心之和称为状态心,并表示为 Core(S)。
    • 同心状态:如果 LR(1) 状态机中的两个状态具有相同的心,则称它们为同心状态。

    • Core(S) 表示状态 S 的心部分;

      Core(S)= { LR0item | [ LR0item, a]∈S }

      就是状态 S 的所有 LR(0) 项目的集合

    • SameCoreState(S): 所有与 S 同心的状态集 (包括 S);

      SameCoreState(S)=S∪{S′ | Core(S′)=Core(S) }

      状态的心相同的 LR(1) 状态

    • Merge(SS): 同心状态集 SS 中所有状态项目的合并

      Merge(SS)= { LR1item | LR1item∈S, S∈SS }

    例子
    假设在 LR(1) 状态机中有状态 S1 和 S2:

      S1 = { [A→a•b, a1 ], [B→p•, b1 ] },
    
      S2 = { [A→a•b, a2 ], [B→p•, b2 ] }
    
    • Core(S1)= { A→a•b, B→p• },
    • Core(S2)= { A→a•b, B→p• } ,
    • SameCoreState( S1 )= { S1, S2 }
    • Merge({S1, S2}) = { [A→a•b, {a1, a2} ], [B→p•, {b1 ,b2}] }

    由 LR(1)SM 构造 LALR(1) 可归前缀状态机

    有两种构造方式:

    1. 用 LR(1) 状态机来构造:合并 LR(1) 状态机中的等价状态。
      具体描述:按 LR(1) 状态机的方式构造,但发现同心状态时不产生新状态,而是采用合并状态的方法。
    2. 用 LR(0) 状态机来构造:用传播方式求出每个项目的展望符集。

    下面只介绍第一种构造方式:

    例子:
    设有文法 G:

    1. Z→bMb
    2. M→a
    3. M→(L
    4. L→Ma)

    img

    其中(4, 9) (5,11) (6, 12) (7, 13) (8, 14) (10, 15) 是可合并的同心状态。

    合并后得到:

    LALR(1) 分析表的构造

    LALR(1) 分析表的构造算法和 LR(1) 分析表的构造算法一样。

    LALR(1) 分析条件 | 定义

    如果合并同心状态后得到的 LALR(1) 状态机中没有冲突,则称其是 LALR(1) 文法。

    合并同心状态带来的问题

    img

    因为文法是 LR(1) 文法,所以 S1、S2 都不存在移入-归约冲突和归约-归约冲突。所以:

    {u1∪v1}∩a、{u2∪v2}∩a = ∅ u1∩v1、u2∩v2 = ∅

    合并后:

    {u1∪v1∪u2∪v2}∩a = ∅

    没有移入-归约冲突。但是不代表 u1∩u2 和 v1∩v2= ∅。可能产生归约-归约冲突

    例子:有如下文法:

    1. Z→aAd
    2. Z→bAc
    3. Z→aBc
    4. Z→bBd
    5. A→e
    6. B→e

    img

    LR(1) 可归前缀图中无冲突,但是将 3、4 状态合并以后,产生归约-归约冲突。同时延迟发现错误。

    LALR(1) 方法

    它具有 SLR(1) 的 状态数少 的优点和 LR(1) 的 适用范围广 的优点。

    LALR(1) 方法的功能介于 SLR(1) 和 LR(1) 之间。

    LALR(1) 状态机的状态个数和 LR(0) 状态机的状态个数相同,而其展望符则既不采用 SLR(1) 的 Follow 集方法,也不采用 LR(1) 的完全精确法。

    LR 方法总结

    • LR(0) 分析方法是最简单的一种,也是建立其他 LR 分析法的基础,但它的缺点是分析能力低,局限性大;
    • SLR(1) 分析方法是一种比较容易实现又极有实用价值的方法,但是有一些文法不能构造出 SLR(1) 分析表;
    • LR(1) 分析方法分析能力最强,能够适应大多数语言的文法,缺点是实现代价过高;
    • LALR(1) 分析方法分析能力介于 SLR(1) 和 LR(1) 之间,合并同心状态后对某些错误发现的时间会产生推迟现象,但仍能够找到错误出现的位置,一般来说 LALR(1) 分析表所占的空间要比原 LR(1) 分析表小得多。

    下面从『功能和状态数』两个方面来具体讨论这 4 种分析方法的优缺点。

    • 从功能上看,各种语法分析方法的分析能力从小到大依次为:
    LR(0) < SLR(1) < LALR(1) < LR(1)
    • 从活前缀状态机的状态数方面考虑:
      • LR(0) 和 SLR(1) 分析用的都是 LR(0) 状态机,因此它们的状态数相等;
      • LALR(1) 分析是把 LR(1) 状态机中的同心状态合并,显然合并同心状态后的 LALR(1) 状态机的状态个数和SLR(1) 状态机的状态数是相等的;
      • 因此从状态数方面看,各种语法分析方法的状态数有如下关系:
    LR(0) = SLR(1) = LALR(1) < LR(1)。
    展开全文
  • lr_stop_transaction() 终止执行的事务运行。 double lr_stop_transaction(const char * transaction_name);   参数说明: transaction_name打开的事务的名称。 保留此功能是为了向后兼容。有关报告部分...

    lr_stop_transaction()

    终止执行的事务运行。

    double lr_stop_transaction(const char * transaction_name);

     

    参数说明:

    transaction_name打开的事务的名称。

    保留此功能是为了向后兼容。有关报告部分事务持续时间的更一般方法,请参阅lr_start_sub_transaction和lr_set_transaction。

    调用lr_stop_transaction后,由“get”事务函数返回的统计信息只反映直到调用的数据,直到调用lr_resume_transaction。指定的事务必须已使用lr_start_transaction打开。

    但是,数据收集不会中断。调用lr_resume_transaction后,“get”函数自事务开始后返回所有数据。此外,分析测试时的最终结果将反映总值,包括交易停止和恢复之间的期间。

    如果未调用lr_resume_transaction,则“get”函数和最终结果仅反映从事务开始到lr_stop_transaction调用的持续时间。因此,可以有条件地使用lr_stop_transaction和lr_resume_transaction来收集关于整个事务或仅开始的信息。要完成此任务,只有满足条件时才调用lr_resume_transaction。

    注意:当以这种方式收集数据时,分析中的数据将反映满足lr_resume_transaction的条件的测试,以及不满足条件的测试。

    如果您希望排除的部分在事务结束之前结束,则此技术不适用。如果需要区分满足lr_resume_transaction条件的情况和不在分析数据中的情况,则此技术不适用。

     

    例如:lr_stop_transaction和lr_resume_transaction

    在下面的示例中,将停止并恢复事务,以显示对所报告的时间的影响.。

    {

        int i;

        int iteration = 100;

        char aStr [100];

        lr_start_transaction(“Stop&Start”);

        / *做业务流程的一部分。代表

            这里通过循环来保证一些

            时间流逝。 * /

        for(i = 0; i <iteration; ++ i){

            sprintf(aStr,“%d”,i);

                lr_log_message(“%d”,i);

        }}

        / *输出持续时间到此点(0.343750秒)

            与事务活动。 * /

        lr_output_message(“First time =%f”,

            lr_get_transaction_duration(“Stop&Start”));

        //停止事务

        lr_stop_transaction(“Stop&Start”);

        / *将持续时间输出到此点(0.359375秒)

            与事务停止。 * /

        lr_output_message(“停止后立即=%f”,

            lr_get_transaction_duration(“Stop&Start”));

        //确保一些时间过去

        for(i = 0; i <iteration; ++ i){

            sprintf(aStr,“%d”,i);

            lr_log_message(“%d”,i);

        }}

        / *将持续时间输出到此点(0.359375秒)

            交易仍然停止,但是

            更多的时间。注意时间是一样的。

            在事务处理时在循环中花费的时间

            停止不报告。 * /

        lr_output_message(“停止和循环=%f”后,

            lr_get_transaction_duration(“Stop&Start”));

        //恢复事务

        lr_resume_transaction(“Stop&Start”);

        / *请注意,随着事务恢复,

            自从开始以来所有的时间都过去了

            报告(0.781250秒),包括花费的时间

            在事务被停止的循环中。 * /

        lr_output_message(“恢复时间=%f”后,

            lr_get_transaction_duration(“Stop&Start”));

        //将时间添加到持续时间

        for(i = 0; i <iteration; ++ i){

            sprintf(aStr,“%d”,i);

            lr_log_message(“%d”,i);

            }}

        / *报告的总时间(1.140625秒)* /

        lr_output_message(“resume resume and loop =%f”后,

            lr_get_transaction_duration(“Stop&Start”));

         

        //结束事务

        lr_end_transaction(“Stop&Start”,LR_AUTO);

        return 0;

    }}

    输出:

    Action.c(11):通知:事务停止和启动开始。

    Action.c(23):第一次= 0.343750

    Action.c(27):通知:事务停止和开始停止。

    Action.c(31):停止后立即= 0.359375

    Action.c(45):停止和循环后= 0.359375

    Action.c(49):通知:事务停止和启动恢复。

    Action.c(55):恢复时间= 0.781250

    Action.c(65):resume和循环后= 1.140625

    Action.c(70):通知:事务停止和开始以通过状态结束(持续时间:1.1406)。

    展开全文
  • LR工具使用

    2019-10-05 05:27:55
    lr的作用: Lr是一个性能测试工具,主要就是用来通过这个工具来对系统前后台进行压力测试,然后根据测试的结果来综合分析系统存在的性能问题。 一般系统综合来说的性能测试大致流程: Lr的安装: 下载lr11或者...
  • GBDT+LR

    千次阅读 2017-09-07 10:17:59
    GBDT+LR
  • [机器学习] LR与SVM的异同

    千次阅读 2018-09-02 09:24:12
    1 为什么将LR和SVM放在一起来进行比较? 回答这个问题其实就是回答LR和SVM有什么相同点。 第一,LR和SVM都是分类算法。 看到这里很多人就不会认同了,因为在很大一部分人眼里,LR是回归算法。我是非常不赞同这...
  • lr_stop_transaction_instance() 终止由其句柄指定的事务实例的报告统计信息。 double lr_stop_transaction_instance(long parent_handle);   参数说明: parent_handle:唯一的事务句柄。   调用lr_...
  • 这里可以直接跑通的github:wangru8080/gbdt-lr 1 GBDT + LR原理 参考:GBDT+LR算法解析及Python实现 1.1 CTR常见流程 GBDT+LR 使用最广泛的场景是CTR点击率预估,即预测当给用户推送的广告会不会被用户点击。 点击...
  • lr_user_data_point() 记录用户定义的数据样本。 int lr_user_data_point(const char * sample_name,double value);   参数说明: sample_name:数据点名称。 value:要记录的值。   lr_user_data_...
  • 数学:LR推导

    2019-03-18 08:04:32
    逻辑斯蒂分布 F(x) = P(X &lt;= x) = 1 / ( 1 + e^{ - (x - u) / r } ) f(x) = F’(x) = e ^ { -(x-u)/r } / r*(1 + e ^ { - (x - u) / r } )^2 u位置参数 r形状参数 LR表达式: ...LR分类问题本质...
  • GBDT_LR融合

    2020-05-19 23:58:22
    文章目录GBDT_LR融合 GBDT_LR融合
  • LR技术——LR(0)自动机的构建

    千次阅读 多人点赞 2019-06-17 14:26:48
    #LR语法分析器 特点: 1)由表格驱动 2)几乎适用所有程序设计语言 3)无回溯的移入归约技术 4)可以尽早检测到错误 #项 什么是项?这里所说的项是一种状态,用来在LR语法分析中对集合进行描述。 例如产生式 A ->...
  • lr-svm

    2016-08-25 21:18:22
    LR-SVM(有待重新整理) 参考:http://www.zhihu.com/question/26768865 总结: 1)在线学习:SVM不支持在线学习,LR支持 2)不平衡数据:SVM不依赖于数据的分布,所以数据是否平衡影响不是很大(有影响的)...
  • GBDT+LR CTR

    2020-03-09 19:51:57
    GBDT+LR算法解析及Python实现 1. GBDT + LR 是什么 本质上GBDT+LR是一种具有stacking思想的二分类器模型,所以可以用来解决二分类问题。这个方法出自于Facebook 2014年的论文 Practical Lessons from Predicting ...
  • 写个LR的总结吧

    2021-03-23 15:14:52
    弄清楚压力测试的主要关注点:(区分关注压力还是负载)  (1)压力测试:  a.被测对象最多可以承受多少用户后,效率开始降低或者异常(Vuser)  b.被测对想可以最多处理多少事物后,效率开始降低或者异常(Transaction&...
  • 逻辑回归算法LR

    2019-06-21 18:00:32
    逻辑回归算法LR 逻辑回归相信很多人都很熟悉,这个算法科能不如随机森林、SVM、神经网络、GBDT等分类算法那么复杂那么高深,但是绝不能小瞧这个算法,因为这个这个算法有几个优点是那几个算法无法达到的,一是逻辑...
  • loadrunner Lr_类函数之 lr_resume_transaction()
  • Python GBDT+LR

    2019-12-09 13:51:24
    通过roc-auc曲线来比对不同算法组合的效果,算法包括:RT+LR、RF、RF+LR、GBDT、GBDT+LR,结果表明,通过GBDT算法fit后,进行OneHot transformation,然后再进行LR Classification的效果是最好的。 在工业生产上,也...
  • 浅析GBDT+LR

    2021-04-12 01:44:29
    最近在做的项目中,一直用到了GBDT+LR算法模型来做二分类,好好梳理了下。下面介绍下这个算法。先来解答下面三个问题。 GBDT+LR模型是什么:是一种具有stacking思想的二分类器模型,所以可以用来解决二分类问题。这...
  • GBDT + LR 是什么,用在哪 二、说明 GBDT + LR 的结构 RF + LR ? Xgb + LR? GBDT + LR 模型提升 三、实践 1 如何获得样本落在哪个叶子节点 2 举例 2.2.1 训练集准备 2.2.2 RF+LR 2.2.3 GBDT+LR 2.2.4 ...
  • LR SP PC

    2019-06-02 18:28:00
    LR SP PC 深入理解ARM的这三个寄存器,对编程以及操作系统的移植都有很大的裨益。 1、堆栈指针r13(SP):每一种异常模式都有其自己独立的r13,它通常指向异常模式所专用的堆栈,也就是说五种异常模式、非异常...
  • LR模型常见问题小议

    千次阅读 2016-01-10 23:33:04
    经常说的2/8原则,LR肯定就是能解决80%问题中那20%的工具。所以LR还是值得好好研究的。发现以前对LR重视不够,总想着赶紧把其他算法也学了,才能拉小跟同事之间机器学习的gap。其实LR用得还是挺多的,而且效果还是...
  • LR检查点

    2019-09-17 10:36:21
    究竟VeGen是怎么区分脚本是否回放正确呢?一般情况下,脚本回放错误都是应为404错误,也就是页面无法找到,而只要页面返回了,VuGen都不会提示任何错误。例如,我们的飞机购票系统这个例子,因为一开始没做关联而...
  • lr_checkpoint() 根据期望值(检查点)验证参数的值。 int lr_checkpoint(“StepName =&lt;step_name&gt;”,“ActualValue = {&lt;input_param&gt;}”,“Compare =&lt;operator&gt;...
  • 逻辑回归LR vs 支持向量机SVM

    千次阅读 2018-09-27 11:08:09
    LR vs SVM LR和SVM的相同点 LR和SVM的不同点 逻辑回归LR LR损失函数 LR公式推导 支持向量机SVM 间隔和支持向量 对偶问题 核函数 软间隔和正则化 LR vs SVM LR和SVM的相同点 都是监督学习算法(都需要有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,861
精华内容 5,544
关键字:

区分lr