面向特定问题的开源算法管理和推荐(七) | 2021SC@SDUSC

2021SC@SDUSC

面向特定问题的开源算法管理和推荐

一、本文分析目标

前面我们分析了BinaryDocumentIndex类的基本实现,其中以bigram为基础,并提前去除了停用词,每当有新的词进入的时候,就可以将其的文档频率输出。我还分析了源码中为了表征某个bigram的具体位置而创建的结构体:SequencePosition,了解了其组成原理,以及使用方法。

本文我将进一步分析,以便为最终的提取算法作准备。我们将先考察前面BinaryDocumentIndex类中遗留下来的问题:WordIdxBigram类的具体实现,然后再考察,其余部分。

二、WordIdxBigram的原理

这个类位于ElskeLib的Model模块下,说明他是模型的主要组成部分。它的声明也是为了提高效率,而其提高效率的机理就是通过快得比较操作。通过这个类,我们能够将它以两种不同的方式看待:

  • 1.看作一个long;
  • 2.看作两个独立的integer.

类的定义过程中用到了一个[FieldOffset(0)],其作用应该是自定义结构在内存中布局方式。紧接着定义了combine和idx1以及idx2。

[FieldOffset(0)]
private readonly long Combined;
[FieldOffset(0)]
public readonly int Idx1;
[FieldOffset(4)]
public readonly int Idx2;

这样定义的意义在于,每次输入两个整数参数,大小各为4字节,第一个combine变量是这两个整数变量的直接组合,大小为二者之和,刚好为long类型的大小8字节,因此我们将combine位置设置为和Idx1一样的位置0,而Idx2设置为4字节处“[FieldOffset(4)]”。

通过比较输入的另一个WordIdxBigram对象的combine属性,我们就可以将二者进行比较;假如输入的对象不是WordIdxBigram的,则返回false。

public override bool Equals(object obj)
{
if (obj is WordIdxBigram p)
    	return Equals(p);
return false;
}

public bool Equals(WordIdxBigram other)
{
return Combined == other.Combined; 
}

接下来重定义==和!=运算符,以及输出方法。

public static bool operator ==(WordIdxBigram pair1, WordIdxBigram pair2)
{
return pair1.Equals(pair2);
}

public static bool operator !=(WordIdxBigram pair1, WordIdxBigram pair2)
{
return !pair1.Equals(pair2);
}

public override string ToString()
{
return $"{Idx1}|{Idx2}";
}

最后是一个GetHashCode()函数,其主要目的是将这个数值映射到一个hash表上,它更加复杂,但是效率更高,并且能减少碰撞数量。

public override int GetHashCode()
{
unchecked
{
return(int) ((((2166136261u ^ (uint)Idx1) * 16777619u) ^ (uint)Idx2) ); //return (Idx1 * 397) ^ Idx2;
	}
}

三、WordIdxMap的实现与与原理

1. 概述

接下来我们考察如何实现WordIdxMap。这个类也是位于ElskeLib的Model模块下的。根据注释信息我们不难知道,这个类将独立的词语映射成他们独特的数值表达。其中包含一些方法,可以将文档进行分词,然后将其的每一个词映射成为数值。

2.基本数据定义

我们仍然从WordIdxMap的基本数据定义开始阅读。

首先是一个只读的MemoryComparer,其类型相对复杂,是CharRoMemoryContentEqualityComparer,我们可以在文件的末尾找到这个类,它实现了一个比较两个类是否相等的IEqualityComparer接口,其具体比较的是一个只读的内存区域ReadOnlyMemory,并且其区域中存储的数据为char。因此,这个MemoryComparer,可以简单地理解为用于比较的一个对象。

private static readonly CharRoMemoryContentEqualityComparer MemoryComparer = new();

随后是一个分词设置,其所对应的类TokenizationSettings也在文件末尾有相应的定义。包含以下内容,其中每一项为一个布尔型变量:

bool型变量设置内容
ConvertToLowercase是否都转化为小写字母
RetainPunctuationCharacters保留停顿符号比如句号,他们会被额外分出
HtmlDecode对HTML-specific 编码例如 &amp进行解码
TwitterRemoveRetweetInfoTwitter去除转发信息
TwitterRemoveHashtagsTwitter去除标签,以#开头
TwitterStripHashtagsinxTwitter去除标签中的#
TwitterRemoveUserMentionsTwitter去除@user项
TwitterRemoveUrlsTwitter去除http://… 和 https://…

可以看到有很大一部分内容是关于特定领域的分词细节。在这里尤其表现为对Twitter的分词设置。

/// <summary>
/// Settings that configure how documents will be tokenized. Should not be changed
/// after the map has been populated to avoid inconsistencies.
/// </summary>
public TokenizationSettings TokenizationSettings { get; set; } = new();

接下来是一个字典_wordToIdx,它是这个类的核心部分,将一个个词项转化成对应的数值项。_idxToWord是一个列表,将index映射到word上。最后再设置一把自旋锁:_spinLock。

Count表示具体的分词数量(互不相同的词语)

private readonly Dictionary<ReadOnlyMemory<char>, int> _wordToIdx = new(MemoryComparer);
private readonly List<ReadOnlyMemory<char>> _idxToWord = new();
private SpinLock _spinLock = new();

/// <summary>
/// Number of tokens (= words) in this map.
/// </summary>
public int Count => _idxToWord.Count;

3. TokenizeDocument函数

这个函数主要作用是根据之前的TokenizationSettings中的具体参数,将给定的文档分词。可以看到这个函数中调用了大量的Tokenizer文件中的函数。

if (TokenizationSettings.HtmlDecode)
document = WebUtility.HtmlDecode(document);
if (TokenizationSettings.ConvertToLowercase)
document = document.ToLowerInvariant();

var words = document.SplitSpaces();
if (TokenizationSettings.TwitterRemoveHashtags
|| TokenizationSettings.TwitterRemoveRetweetInfo
|| TokenizationSettings.TwitterRemoveUrls
|| TokenizationSettings.TwitterRemoveUserMentions
|| TokenizationSettings.TwitterStripHashtags)

words = words.CleanTweets(TokenizationSettings.TwitterRemoveHashtags,
TokenizationSettings.TwitterRemoveUserMentions,
  	TokenizationSettings.TwitterRemoveUrls, TokenizationSettings.TwitterRemoveRetweetInfo,
    	TokenizationSettings.TwitterStripHashtags);
words = words.Tokenize();
if (!TokenizationSettings.RetainPunctuationCharacters)
words = words.RemovePunctuationChars();
            
return words;

下表分别说明了每个调用的具体作用,我们暂时将不再对其内部结构进行细致分析。

函数句柄具体作用
SplitSpaces按照空格分词
CleanTweets按照参数清理推文
Tokenize分词
RemovePunctuationChars去除标点符号

四、总结

本文主要分析了WordIdxBigram以及WordIdxMap两个类,其中第二个还有部分函数没有完全分析。我们通过读源码可以发现,WordIdxMap中调用到了Tokenizer文件中的部分函数,而由于Tokenizer涉及到底层的字符表示等内容,我们在此不进行更多的分析。

后文,我们将分析WordIdxMap的更多函数实现,包括DocumentToIndexes等。具体细节将在后续内容中展开。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值