基于结构化 SVM 进行序列标注

2018 年 2 月 5 日 AI研习社 seaboat


作者 seaboat ,本文作者为雷锋网 AI 研习社撰写的独家稿件,未经雷锋网 AI 研习社许可不得转载。

关于SVM

SVM 即支持向量机,常用于二分类模型。它主要的思想是:

1.它是特征空间上间隔最大的线性分类器;

2.对于线性不可分的情况,通过非线性映射算法将低维空间的线性不可分的样本映射到高维特征空间,高维特征空间能够进行线性分析。

  什么是结构化

其实机器学习中,如果按照输出空间不同可以分为:

  1. 二元分类 (binary classification)

  2. 多元分类 (multiclass classification)

  3. 回归问题 (regression)

  4. 结构化预测 (structured prediction)

其中前面三类都是我们常见且经常用的,第四种结构化预测重点体现在结构化上,前面三类的输出都是标签类别或者回归值之类的单变量,而结构化预测输出是一种结构化的数据结构,比如输入一句话,输出是一颗语法树。此外,结构还可以是图结构、序列结构等。

  结构化 SVM

把前面的 SVM 与结构化结合起来就是结构化 SVM 了。它为了处理更加复杂的彼此之间互相存在依赖关系的结构数据,对传统 SVM 进行了改进,可以说结构化 SVM 是在传统 SVM 的基础上扩展出来的。结构化 SVM 使用时主要涉及学习和推理两个过程,与大多数机器学习算法一样,学习其实就是确定模型的参数的过程,而推理就是根据学习到的模型对给定的输入进行预测的过程。

假设给定了训练集,其中 X 和 Y 是两个集合,结构化 SVM 就是通过这些样本来训练一个输入输出对的函数。预测时,对于给定的输入  x ,在所有 yY 中取得最大值的 y 即为预测项。

  学习过程

学习结构化数据就是要找到上述的一个判别函数,使之在判别函数确定后,对给定的输入 x ,能选择最大化函数 f 值的 Y 作为输出。假定函数 f 的形式为,

其中判别函数,w 是参数向量,而 Ψ(x,y) 可以看成是输入输出对的特征表示,代表将输入输出对合并起来的特征向量,它的形式取决于具体问题。 一般会假设  F(x,y;w)  是 (x,y) 和参数向量 w w 的线性函数,即 

接着还得再定义一个损失函数Δ:Y×YR,它应该满足时 ,当 时 那么有经验风险函数,

所以我们的目标是要找到一个  使得经验风险函数最小,而它可能存在经验风险为 0 的情况,此时,满足如下条件

其中,。根据间隔最大化来求解,固定 w 的长度,求能使得间隔最大的w。两个超平面的距离为最大化其实就等价于最小化,这时已经可以转成 SVM 中问题的形式了,

但实际情况中经验风险为 0 可能会导致过拟合现象,这时要考虑容忍训练集中某些样本错误分类,从而引入松弛变量,于是优化问题变为:

约束条件引入损失函数的影响,得

那么现在不管是经验风险为 0 还是不为 0,剩下要做的事就是求解上述优化问题,即根据上述各个式子中的约束条件解得最优值 W。怎么求解还是个难题,如果样本数较少且 Y 状态数较少,能用传统的二次优化求解。

而实际情况中样本数和状态数都较多,于是产生的约束条件规模非常大,总数量为,其中 n 为样本数,|Y | 为 y 可能的状态数。所以在求解过程中需要先将上述优化问题转换成对偶形式,采用割平面训练法,具体优化过程不考虑所有约束条件,从无约束问题出发,逐步选择约束直到精度满足期望后停止。

  IOB 标记

常用的标注策略有 IOB 标记,即块的第一个符号处标注为 B,块内部的符号标注为 I,块外的符号标注 O。其中 B 为 Begin,表示开始;I 为 Intermediate,表示中间;O 为 Other,表示其他。比如:

我明天去北京。
OBIOBI

  实现例子

使用 dlib 库实现结构化 SVM 序列标注功能,以下仅仅是一个简单的功能。对 “我 昨 天 在 学 校 看 到 小 明”,“小 红 刚 刚 才 去 晚 自 习” 中的人名进行标注,并且使用 BIO 标记方式,通过训练后对 “我 昨 天 在 学 校 见 到 大 东” 句子进行人名提取。

输出分别为

小 明
小 红
大 东

#include <iostream>
#include <cctype>
#include <dlib/svm_threaded.h>
#include <dlib/string.h>

using namespace std;
using namespace dlib;

class feature_extractor
{

public:
 typedef std::vector<std::string> sequence_type;

 const static bool use_BIO_model = true;
 const static bool use_high_order_features = true;
 const static bool allow_negative_weights = true;
 unsigned long window_size()  const { return 3; }

 unsigned long num_features() const { return 1; }

 template <typename feature_setter>
 void get_features(
   feature_setter& set_feature,
   const sequence_type& sentence,
   unsigned long position
   )
const
 
{
   if (sentence[0].compare("小") == 0 || sentence[0].compare("明") == 0 || sentence[0].compare("红") == 0 || sentence[0].compare("张") == 0 || sentence[0].compare("陈") == 0)
     set_feature(0);
 }
};


void make_training_examples(
 std::vector<std::vector<std::string> >& samples,
 std::vector<std::vector<std::pair<unsigned long, unsigned long> > >& segments
 )

{
 std::vector<std::pair<unsigned long, unsigned long> > names;

 samples.push_back(split("我 昨 天 在 学 校 看 到 小 明"));
 names.push_back(make_pair(8, 10));
 segments.push_back(names); names.clear();

 samples.push_back(split("小 红 刚 刚 才 去 晚 自 习"));
 names.push_back(make_pair(0, 2));
 segments.push_back(names); names.clear();

}

void print_segment(
 const std::vector<std::string>& sentence,
 const std::pair<unsigned long, unsigned long>& segment
 )

{
 for (unsigned long i = segment.first; i < segment.second; ++i)
   cout << sentence[i] << " ";
 cout << endl;
}

int main()
{
 std::vector<std::vector<std::string> > samples;
 std::vector<std::vector<std::pair<unsigned long, unsigned long> > > segments;
 make_training_examples(samples, segments);

 structural_sequence_segmentation_trainer<feature_extractor> trainer;

 sequence_segmenter<feature_extractor> segmenter = trainer.train(samples, segments);

 for (unsigned long i = 0; i < samples.size(); ++i)
 {
   std::vector<std::pair<unsigned long, unsigned long> > seg = segmenter(samples[i]);
   for (unsigned long j = 0; j < seg.size(); ++j)
   {
     print_segment(samples[i], seg[j]);
   }
 }

 std::vector<std::string> sentence(split("我 昨 天 在 学 校 见 到 大 东"));
 std::vector<std::pair<unsigned long, unsigned long> > seg = segmenter(sentence);
 for (unsigned long j = 0; j < seg.size(); ++j)
 {
   print_segment(sentence, seg[j]);
 }

}

NLP 工程师入门实践班:基于深度学习的自然语言处理

三大模块,五大应用,手把手快速入门 NLP

海外博士讲师,丰富项目经验

算法 + 实践,搭配典型行业应用

随到随学,专业社群,讲师在线答疑

▼▼▼




新人福利




关注 AI 研习社(okweiwu),回复  1  领取

【超过 1000G 神经网络 / AI / 大数据,教程,论文】



开发者自述:我是怎样理解支持向量机(SVM)与神经网络的

登录查看更多
1

相关内容

在机器学习中,支持向量机(SVM,也称为支持向量网络)是带有相关学习算法的监督学习模型,该算法分析用于分类和回归分析的数据。支持向量机(SVM)算法是一种流行的机器学习工具,可为分类和回归问题提供解决方案。给定一组训练示例,每个训练示例都标记为属于两个类别中的一个或另一个,则SVM训练算法会构建一个模型,该模型将新示例分配给一个类别或另一个类别,使其成为非概率二进制线性分类器(尽管方法存在诸如Platt缩放的问题,以便在概率分类设置中使用SVM)。SVM模型是将示例表示为空间中的点,并进行了映射,以使各个类别的示例被尽可能宽的明显间隙分开。然后,将新示例映射到相同的空间,并根据它们落入的间隙的侧面来预测属于一个类别。

知识荟萃

精品入门和进阶教程、论文和代码整理等

更多

查看相关VIP内容、论文、资讯等
基于深度学习的表面缺陷检测方法综述
专知会员服务
92+阅读 · 2020年5月31日
【人大】图实现算法综述与评测分析
专知会员服务
37+阅读 · 2020年4月28日
【经典书】精通机器学习特征工程,中文版,178页pdf
专知会员服务
347+阅读 · 2020年2月15日
KGCN:使用TensorFlow进行知识图谱的机器学习
专知会员服务
80+阅读 · 2020年1月13日
从零推导支持向量机 (SVM)
AI科技评论
9+阅读 · 2019年2月7日
详解常见的损失函数
七月在线实验室
20+阅读 · 2018年7月12日
专栏 | Bi-LSTM+CRF在文本序列标注中的应用
机器之心
14+阅读 · 2018年1月3日
一种基于序列标注的MOOC知识点抽取方法
科技创新与创业
15+阅读 · 2017年11月10日
机器学习(18)之支持向量机原理(三)线性不可分支持向量机与核函数
机器学习算法与Python学习
3+阅读 · 2017年9月23日
机器学习(13)之最大熵模型详解
机器学习算法与Python学习
7+阅读 · 2017年8月24日
Python · SVM(三)· 核方法
机器学习研究会
7+阅读 · 2017年8月8日
深度学习知识总结(一)
深度学习探索
8+阅读 · 2017年7月18日
Arxiv
21+阅读 · 2019年8月21日
Arxiv
7+阅读 · 2019年5月31日
Arxiv
8+阅读 · 2019年3月28日
Symbolic Priors for RNN-based Semantic Parsing
Arxiv
3+阅读 · 2018年9月20日
Arxiv
4+阅读 · 2017年7月25日
VIP会员
相关VIP内容
相关资讯
从零推导支持向量机 (SVM)
AI科技评论
9+阅读 · 2019年2月7日
详解常见的损失函数
七月在线实验室
20+阅读 · 2018年7月12日
专栏 | Bi-LSTM+CRF在文本序列标注中的应用
机器之心
14+阅读 · 2018年1月3日
一种基于序列标注的MOOC知识点抽取方法
科技创新与创业
15+阅读 · 2017年11月10日
机器学习(18)之支持向量机原理(三)线性不可分支持向量机与核函数
机器学习算法与Python学习
3+阅读 · 2017年9月23日
机器学习(13)之最大熵模型详解
机器学习算法与Python学习
7+阅读 · 2017年8月24日
Python · SVM(三)· 核方法
机器学习研究会
7+阅读 · 2017年8月8日
深度学习知识总结(一)
深度学习探索
8+阅读 · 2017年7月18日
相关论文
Arxiv
21+阅读 · 2019年8月21日
Arxiv
7+阅读 · 2019年5月31日
Arxiv
8+阅读 · 2019年3月28日
Symbolic Priors for RNN-based Semantic Parsing
Arxiv
3+阅读 · 2018年9月20日
Arxiv
4+阅读 · 2017年7月25日
Top
微信扫码咨询专知VIP会员