FDDC2018金融算法挑战赛02-A股上市公司公告信息抽取

更新时间 2018年7月11日 By 带着兔子去旅行

信息抽取是NLP里的一个实用内容。该工具的目标是打造一个泛用的自动信息抽取工具。使得没有任何基础的用户,可以通过简单的步骤提取文档(PDF,HTML,TXT)中的信息。该工具使用C#(.Net Core)开发,所以可以跨平台运行。(Python在做大的工程的时候有诸多不便,所以没有使用python语言)

基本环境

  • .NetCore2.1
  • LTP组件:哈工大LTP3.3.2版
  • PDF转TXT工具 pdfminer
  • 分词系统:结巴分词

ltp工具:哈工大LTP工具(ltp.ai)提供的ltp工具,最新版为3.3.4.该工具在windows,max,centos上,srl的训练可能无法正常完成。(dp,ner阶段没有问题)所以这里使用了3.3.2版本。ltp工具的SRL结果中包含了DP和NER的内容,但是暂时保留DP和NER中间XML文件。

pdfminer:请注意处理中文的时候需要额外的步骤,具体方法不再赘述。部分PDF可能无法正确转换,原因CaseByCase。

结巴分词:某些地名,例如"大连",会被误判。这里使用地名辅助字典的方式做纠正。ltp工具没有这个问题。ltp工具和结巴分词功能虽然重复,但是暂时还不能移除结巴分词。

前期准备

  • 使用pdfminer将PDF文件转化为Txt文件
  • 使用哈工大LTP工具,将Txt文件转换为NER,DP,SRL的XML文件

期待文件夹结构

  • html(存放HTML文件目录)
  • pdf(存放PDF文件目录)
  • txt(存放TXT文件目录)
  • dp(存放LTP的DP结果XML目录)
  • ner(存放LTP的NER结果XML目录)
  • srl(存放LTP的SRL结果XML目录)

训练(词语统计)

  • 分析待提取信息自身的特征
  • 分析待提取信息周围语境的特征(LTP工具)
  • 构建置信度体系

词语自身属性

  • 长度
  • 包含词数
  • 首词词性(POS)
  • 词尾

语境

  • 该关键字在 :(中文冒号)之后的场景下,:(中文冒号)前面的内容
  • 包含该关键字的句子中,该关键字的前置动词
  • 包含该关键字的句子中,该关键字是否在角色标识中存在

训练结果例:

协议书(5.180388%)[56]
协议(11.84089%)[128]
合同(58.55689%)[633]
合同书(2.960222%)[32]
买卖合同(3.792784%)[41]
承包合同(12.0259%)[130]
意向书(0.2775208%)[3]
补充协议(1.110083%)[12]
项目(0.2775208%)[3]
书(0.9250694%)[10]
议案(0.2775208%)[3]
)(0.8325624%)[9]

(更多规则持续加入中,同时对于相关度低的规则也会剔除)

这里暂时使用频率最高的前5位作为抽取依据。同时为了保证正确率,部分特征的占比必须超过某个阈值。

以下是中文冒号的一个例子,要求前导词占比在40%以上。

(例如前导词A可以正确抽取10个关键字,前导词B可以抽取5个关键字,前导词C可以抽取15个关键字。则前导词A的占比为33%)

        e.LeadingColonKeyWordList = ContractTraning.ContractNameLeadingDict
.Where((x) => { return x.Value >= 40; }) //阈值40%以上
.Select((x) => { return x.Key + ":"; }).ToArray();

表格

对于大量表格中的关键字,工具也提供了表格统计的功能。主要是统计一下该关键字的表头标题信息。

同时由于表格中的原始数据可能需要通过参照表格标题才能进行比对的情况,这里支持变换器。

    /// <summary>
/// 增发对象训练
/// </summary>
public static void TrainingIncreaseTarget()
{
var TargetTool = new TableAnlayzeTool();
var IncreaseNumberTool = new TableAnlayzeTool();
IncreaseNumberTool.Transform = NumberUtility.NormalizerStockNumber;
var IncreaseMoneyTool = new TableAnlayzeTool();
IncreaseMoneyTool.Transform = MoneyUtility.Format;
TraningDataset.InitIncreaseStock();
var PreviewId = String.Empty;
var PreviewRoot = new HTMLEngine.MyRootHtmlNode();
foreach (var increase in TraningDataset.IncreaseStockList)
{
if (!PreviewId.Equals(increase.id))
{
var htmlfile = Program.DocBase + @"\FDDC_announcements_round1_train_20180518\定增\html\" + increase.id + ".html";
PreviewRoot = new HTMLEngine().Anlayze(htmlfile, "");
PreviewId = increase.id;
}
TargetTool.PutTrainingItem(PreviewRoot, increase.PublishTarget);
IncreaseNumberTool.PutTrainingItem(PreviewRoot, increase.IncreaseNumber);
IncreaseMoneyTool.PutTrainingItem(PreviewRoot, increase.IncreaseMoney);
}
TargetTool.WriteTop(10);
} 增发对象
投资者名称(7.29849%)[546]
股东名称(10.17244%)[761]
发行对象名称(9.089694%)[680]
认购对象(14.86432%)[1112]
发行对象(20.51865%)[1535]
增发数量
获配股数(股)(17.05559%)[1893]
持股数量(股)(7.793495%)[865]
认购数量(股)(5.586089%)[620]
配售股数(股)(5.53203%)[614]
认购股数(股)(3.585909%)[398]
增发金额
认购金额(元)(4.833%)[314]
获配金额(元)(15.94582%)[1036]
2015年度(4.417423%)[287]
2017年1-3月(4.432815%)[288]
2016年度(5.956595%)[387]

除了统计标题之外,还可以通过某个标题下面出现的内容。

下面的例子是看一下增减持方式有哪些:

    /// <summary>
/// 增减持训练
/// </summary>
/// <param name="TraningCnt">训练条数</param>
public static void Traning(int TraningCnt = int.MaxValue)
{
var ChangeMethodTool = new TableAnlayzeTool();
var PreviewId = String.Empty;
var PreviewRoot = new HTMLEngine.MyRootHtmlNode();
int Cnt = 0;
foreach (var stockchange in TraningDataset.StockChangeList)
{
if (!PreviewId.Equals(stockchange.id))
{
var htmlfile = Program.DocBase + @"\FDDC_announcements_round1_train_20180518\增减持\html\" + stockchange.id + ".html";
PreviewRoot = new HTMLEngine().Anlayze(htmlfile, "");
PreviewId = stockchange.id;
Cnt++; if (Cnt == TraningCnt) break;
}
ChangeMethodTool.PutValueTrainingItem(PreviewRoot, new string[]{"减持方式","增持方式"}.ToList());
}
Program.Training.WriteLine("增减持方式");
ChangeMethodTool.WriteTop(10);
} 增减持方式
集中竞价(24.37453%)[6771]
集中竞价交易(33.39573%)[9277]
大宗交易(21.38306%)[5940]
竞价交易(8.884409%)[2468]
合计(0.9287592%)[258]
集中竞价减持(1.670326%)[464]
减持方式(1.313942%)[365]
<null>(1.090752%)[303]
二级市场竞价(1.040354%)[289]
竞价减持(0.705569%)[196]

抽取

采用各种方法抽取数据,务必使得所有数据都抽取出来。根据训练结果从候选值里面获得置信度最大的数据。抽取手段如下:

  • 具有明确先导词
  • NER实体标识
  • 具体语境

表格抽取工具(内容系)

代码内置表头规则系的表抽取工具,对于表格可以设定如下抽取规则:

  • Content:匹配内容
  • IsContentEq:内容匹配规则(包含或者相等)
    /// <summary>
/// 表抽取规则(内容系)
/// </summary>
public struct TableSearchContentRule
{
/// <summary>
/// 匹配内容
/// </summary>
public List<String> Content;
/// <summary>
/// 是否相等模式
/// </summary>
public bool IsContentEq;
}

下面是一个表格抽取的例子:

        var rule = new TableSearchContentRule();
rule.Content = new string[] { "集中竞价交易", "竞价交易", "大宗交易", "约定式购回" }.ToList();
rule.IsContentEq = true;
var result = HTMLTable.GetMultiRowsByContentRule(root,rule);

表格抽取工具(表头规则系)

代码内置表头规则系的表抽取工具,对于表格可以设定如下抽取规则:

  • SuperTitle:层叠表头的情况下,父表头文字
  • IsSuperTitleEq:父表头文字匹配规则(包含或者相等)
  • Title:表头文字
  • IsTitleEq:表头文字匹配规则(包含或者相等)
  • IsRequire:在行单位抽取时,该项目是否为必须项目
  • ExcludeTitle:表标题不能包含的文字
  • Normalize:抽取内容预处理器
  /// <summary>
/// 表抽取规则
/// </summary>
public struct TableSearchTitleRule
{
public string Name;
/// <summary>
/// 父标题
/// </summary>
public List<String> SuperTitle;
/// <summary>
/// 是否必须一致
/// </summary>
public bool IsSuperTitleEq;
/// <summary>
/// 标题
/// </summary>
public List<String> Title;
/// <summary>
/// 是否必须一致
/// </summary>
public bool IsTitleEq;
/// <summary>
/// 是否必须
/// </summary>
public bool IsRequire;
/// <summary>
/// 表标题不能包含的文字
/// </summary>
public List<String> ExcludeTitle;
/// <summary>
/// 抽取内容预处理器
/// </summary>
public Func<String, String, String> Normalize;
}

下面是一个表格抽取的例子:

增持前 (合并表头) 增持后 (合并表头)
持股数 持股比例 持股数 持股比例

这里我们想抽取持股比例和持股数,但是希望抽取的是增持后的部分,所以需要使用SuperTitle的规则了。

        var HoldList = new List<struHoldAfter>();
var StockHolderRule = new TableSearchRule();
StockHolderRule.Name = "股东全称";
StockHolderRule.Title = new string[] { "股东名称", "名称", "增持主体", "增持人", "减持主体", "减持人" }.ToList();
StockHolderRule.IsTitleEq = true;
StockHolderRule.IsRequire = true; var HoldNumberAfterChangeRule = new TableSearchRule();
HoldNumberAfterChangeRule.Name = "变动后持股数";
HoldNumberAfterChangeRule.IsRequire = true;
HoldNumberAfterChangeRule.SuperTitle = new string[] { "减持后", "增持后" }.ToList();
HoldNumberAfterChangeRule.IsSuperTitleEq = false;
HoldNumberAfterChangeRule.Title = new string[] {
"持股股数","持股股数",
"持股数量","持股数量",
"持股总数","持股总数","股数"
}.ToList();
HoldNumberAfterChangeRule.IsTitleEq = false; var HoldPercentAfterChangeRule = new TableSearchRule();
HoldPercentAfterChangeRule.Name = "变动后持股数比例";
HoldPercentAfterChangeRule.IsRequire = true;
HoldPercentAfterChangeRule.SuperTitle = HoldNumberAfterChangeRule.SuperTitle;
HoldPercentAfterChangeRule.IsSuperTitleEq = false;
HoldPercentAfterChangeRule.Title = new string[] { "比例" }.ToList();
HoldPercentAfterChangeRule.IsTitleEq = false; var Rules = new List<TableSearchRule>();
Rules.Add(StockHolderRule);
Rules.Add(HoldNumberAfterChangeRule);
Rules.Add(HoldPercentAfterChangeRule);
var result = HTMLTable.GetMultiInfoByTitleRules(root, Rules, false);

EntityProperty对象

EntityProperty对象属性如下:

  • PropertyName:属性名称
  • PropertyType:属性类型(数字,金额,字符,日期)
  • MaxLength:最大长度
  • MinLength:最小长度
  • MaxLengthCheckPreprocess:最大长度判定前预处理器(不改变抽取内容)
  • LeadingColonKeyWordList:先导词(包含":")
  • LeadingColonKeyWordCandidatePreprocess:先导词预处理器(改变抽取内容
  • QuotationTrailingWordList:引号和书名号中的词语
  • DpKeyWordList:句法依存环境
  • ExternalStartEndStringFeature:普通的开始结尾词判定
  • CandidatePreprocess:一般候选词预处理器(改变抽取内容
  • ExcludeContainsWordList:不能包含词语列表
  • ExcludeEqualsWordList:不能等于词语列表
  • Confidence:置信度对象
    /// <summary>
/// 获得合同名
/// </summary>
/// <returns></returns>
string GetContractName()
{
var e = new EntityProperty();
e.PropertyName = "合同名称";
e.PropertyType = EntityProperty.enmType.Normal;
e.MaxLength = ContractTraning.MaxContractNameLength;
e.MinLength = 5;
e.LeadingColonKeyWordList = new string[] { "合同名称:" };
e.QuotationTrailingWordList = new string[] { "协议书", "合同书", "确认书", "合同", "协议" };
e.QuotationTrailingWordList_IsSkipBracket = true; //暂时只能选True
var KeyList = new List<ExtractPropertyByDP.DPKeyWord>();
KeyList.Add(new ExtractPropertyByDP.DPKeyWord()
{
StartWord = new string[] { "签署", "签订" }, //通过SRL训练获得
StartDPValue = new string[] { LTPTrainingDP.核心关系, LTPTrainingDP.定中关系, LTPTrainingDP.并列关系 },
EndWord = new string[] { "补充协议", "合同书", "合同", "协议书", "协议", },
EndDPValue = new string[] { LTPTrainingDP.核心关系, LTPTrainingDP.定中关系, LTPTrainingDP.并列关系, LTPTrainingDP.动宾关系, LTPTrainingDP.主谓关系 }
});
e.DpKeyWordList = KeyList; var StartArray = new string[] { "签署了", "签订了" }; //通过语境训练获得
var EndArray = new string[] { "合同" };
e.ExternalStartEndStringFeature = Utility.GetStartEndStringArray(StartArray, EndArray);
e.ExternalStartEndStringFeatureCandidatePreprocess = (x) => { return x + "合同"; };
e.MaxLengthCheckPreprocess = str =>
{
return EntityWordAnlayzeTool.TrimEnglish(str);
};
//最高级别的置信度,特殊处理器
e.LeadingColonKeyWordCandidatePreprocess = str =>
{
var c = Normalizer.ClearTrailing(TrimJianCheng(str));
return c;
}; e.CandidatePreprocess = str =>
{
var c = Normalizer.ClearTrailing(TrimJianCheng(str));
var RightQMarkIdx = c.IndexOf("”");
if (!(RightQMarkIdx != -1 && RightQMarkIdx != c.Length - 1))
{
//对于"XXX"合同,有右边引号,但不是最后的时候,不用做
c = c.TrimStart("“".ToCharArray());
}
c = c.TrimStart("《".ToCharArray());
c = c.TrimEnd("》".ToCharArray()).TrimEnd("”".ToCharArray());
return c;
};
e.ExcludeContainsWordList = new string[] { "日常经营重大合同" };
//下面这个列表的根据不足
e.ExcludeEqualsWordList = new string[] { "合同", "重大合同", "项目合同", "终止协议", "经营合同", "特别重大合同", "相关项目合同" };
e.Extract(this); //是否所有的候选词里面包括(测试集无法使用)
var contractlist = TraningDataset.ContractList.Where((x) => { return x.id == this.Id; });
if (contractlist.Count() > 0)
{
var contract = contractlist.First();
var contractname = contract.ContractName;
if (!String.IsNullOrEmpty(contractname))
{
e.CheckIsCandidateContainsTarget(contractname);
}
}
//置信度
e.Confidence = ContractTraning.ContractES.GetStardardCI();
return e.EvaluateCI();
}

鸣谢

感谢阿里巴巴组委会提供标注好的金融数据。

感谢组委会@通联数据_梅洁,@梅童的及时答疑。

感谢微信好友 邓少冬 潘昭鸣 NLP宋老师 的帮助和指导

【开源】C#信息抽取系统【招募C#队友】的更多相关文章

  1. wemall开源商城免费商城系统部分代码(内含代码地址)

    wemall开源商城免费商城系统部分代码,下面分享部分代码,供学习者学习: 开源版把install文件夹下的install.lock删除之后可进行自动安装 后台访问地址:http:// www.xxx ...

  2. 业余草推荐18个Java开源免费的CMS系统

    1.InfoGlue infoglue是一个高级的.可扩展的.健壮的内容管理系统,完全用Java开发.重要的功能包括完全支持多语言,站点之间良好的重用,以及广泛的集成能力. 该项目主页:http:// ...

  3. 开源网站访问统计系统Piwik

    http://www.piwik.cn/ http://www.piwik.org/ Piwik 是一套基于 Php+MySQL 技术构建,能够与 Google Analytics 相媲美的开源网站访 ...

  4. OSSIM(开源安全信息管理系统)在企业网络管理中的应用

    国内首个Ossim技术交流群(179084574),欢迎加入我们 参与51CTO[第242期]OSSIM,企业信息安全管理利器热门技术讨论 650) this.width=650;" bor ...

  5. OS.js – 开源的 Web OS 系统,赶快来体验

    OS.js 是一个开源的 Web OS 系统,可以在浏览器中运行,提供了窗口管理器,应用程序API,用户界面开发套件和抽象的文件系统等.可以部署在 Node 或者 PHP 环境中运行.OS.js is ...

  6. 完成一个MVC+Nhibernate+Jquery-EasyUI信息发布系统

    一.最近学习了Jquery-EasyUI框架,结合之前用过的MVC3+Nhibernate做一个信息发布系统,对工作一年半的自己做一个总结吧!(也正好 供初学者学习!) 二.先上截图(系统简介),让大 ...

  7. Java中正则表达式、模式匹配与信息抽取

    引言 记得几年前在做网页爬虫后的信息抽取时,针对网页源码中隐藏的要提取的信息,比如评论.用户信息等属性信息,直接利用HtmlParser得到.如此做倒是简单,不过利用的是网页的规范的tag标记.其实j ...

  8. MPush开源消息推送系统:简洁、安全、支持集群

    引言由于之前自己团队需要一个消息推送系统来替换JPUSH,一直找了很久基本没有真正可用的开源系统所有就直接造了个轮子,造轮子的时候就奔着开源做打算的,只是后来创业项目失败一直没时间整理这一套代码,最近 ...

  9. trie树信息抽取之中文数字抽取

    这一章讲一下利用trie树对中文数字抽取的算法.trie树是一个非常有用的数据结构,可以应用于大部分文本信息抽取/转换之中,后续会开一个系列,对我在实践中摸索出来的各种抽取算法讲开来.比如中文时间抽取 ...

随机推荐

  1. Unable to load configuration. - action - file:/F:/apache-tomcat-8.0.30/webapps/test1Struts2/WEB-INF/classes/struts.xml:11:71

    Unable to load configuration. - action - file:/F:/apache-tomcat-8.0.30/webapps/test1Struts2/WEB-INF/ ...

  2. 函数模拟sort快排

    设计一个对一维数组进行排序的sort函数,并调用它实现数组排序 思路:函数调用不止调用一个,最主要对函数不熟悉: #include<stdio.h> #define N 10 int ma ...

  3. Ef 自动迁移,日志

    Ef 迁移 在vs打开程序控制台 2,选择程序集 ,如果是初次,输入 Enable-Migrations,启动迁徙 3  添加迁移,完成修改 4,之后会自动生成迁移配置文件. 然后再上下文类中加入 两 ...

  4. python数据结构之队列(一)

    队列概念 队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表. 队列是一种先进先出的(First In First Out)的线性表,简称FIFO.允许插入的一端为队尾,允许 ...

  5. csdn 站点使用

    大学时使用csdn下载软件资源,最终csdn成为一个it中很重要站点,csdn的运营方式值得思考.

  6. web前端知识大纲:系列二 css篇

    web前端庞大而复杂的知识体系的组成:html.css和 javascript 二.css 1.CSS选择器 CSS选择器即通过某种规则来匹配相应的标签,并为其设置CSS样式,常用的有类选择器.标签选 ...

  7. 在Node.js中在保持目录结构的情况下压缩指定目录

    最近在做一个文件升级的功能,需要从下载服务器中指定目录下的文件.在学习了zlib后发现这个模块达不到这个功能 在查找资料后发现后发现 archiver 模块很好用,不过我也发现大部分中文资料没有如果查 ...

  8. Codeforces.472F.Design Tutorial: Change the Goal(构造 线性基 高斯消元)

    题目链接 \(Description\) 给定两个长为\(n\)的数组\(x_i,y_i\).每次你可以选定\(i,j\),令\(x_i=x_i\ \mathbb{xor}\ x_j\)(\(i,j\ ...

  9. DevC++出现[Error] ld returned 1 exit status,如何解决才好呢?

    回答: 代码在vc中没问题,虽然没用过DevC++. 可以把 ld returned 1 exit status前面的详细出错说出来程序是没有问题的,可能的问题最有可能是以下2个1.是你的程序已经在运 ...

  10. vue给methods中的方法传入当前点击行的值

    <template> <!-- 在template中,只能存在一个根组件 --> <div class="event"> <ul> ...