和《爱丽丝梦游仙境》一起探索自然语言处理
作为一个编程迷和文学迷🤓,我最近一直在尝试寻找更多方法将两者结合起来。我目前正在Recurse 中心度过这个暑假,进行自主学习,所以现在似乎是开始研究这张维恩图中重叠部分的好时机。
进入这个兔子洞后,我痴迷于《悲惨世界》中人物的邻接矩阵,并想知道我还能用代码来研究我最喜欢的书籍的哪些内容。
我一直在研究《Python 中的自然语言处理》这本书,也喜欢卡罗尔对语言的运用,包括他倾向于创造词语并依靠上下文和声音象征来使它们易于理解。
考虑到这一点,我在思考如何识别文本中不常见的词或虚构的词。
因此,带着《爱丽丝梦游仙境》的文本,我开始了我的探索(注:“Jabberwocky”实际上出现在《爱丽丝镜中奇遇记》中,因此在研究这个问题之前,我不确定我应该在《爱丽丝梦游仙境》中找到什么(如果有的话)虚构的词语)。
这本书很贴心地提供了一个可以完成这个任务的功能:
def unusual_words(text):
text_vocab = set(w.lower() for w in text if w.isalpha())
english_vocab = set(w.lower() for w in nltk.corpus.words.words())
unusual = text_vocab.difference(english_vocab)
return sorted(unusual)
于是我插上电源试了一下。结果返回了 582 个单词。这看起来太多了,所以我浏览了其中一些。其中很多单词看起来都很常见:eggs、grins、happens、presented。
好吧,这也许比我想象的要难。我有点沮丧,于是尝试使用其他语料库。但这个语料库有25万个单词,它肯定应该包含这些非常常见的单词吧?然后我注意到这些单词有些特别。大多数名词都是复数(例如eggs),动词是变位的(例如presented)。
我想把单词转换成它们最“基本”的形式。名词用单数,动词用不定式。为此,我尝试了NodeBox 英语语言库。这里遇到了一些小问题。例如,is_verb?
并非所有动词变位都返回 true,并且singular
出现了一些意料之外的结果,所以我最终对所有结果都运行了不定式和单数方法,如果它们返回的结果与起始词不同,则返回它们。
由于此方法比原始方法中简单的集合差异慢得多,因此我将其实现为第二遍,仅用于第一次差异中未找到的单词。我重写了unusual_words
如下代码:
def unusual_words(text):
text_vocab = set(w.lower() for w in text if w.isalpha())
english_vocab = set(w.lower() for w in nltk.corpus.words.words())
unusual = text_vocab.difference(english_vocab)
basic_forms = set(basic_word_form(w) for w in unusual)
return sorted(basic_forms.difference(english_vocab))
并编写了一个新方法basic_word_form
,该方法使用了 NodeBox 方法:
def basic_word_form(word):
word = word.lower()
singular = en.noun.singular(word)
if singular != word:
return singular
else:
infinitive = en.verb.infinitive(word)
return infinitive if len(infinitive) > 0 else word
这样我就得到了一个包含 92 个单词的列表。还不错。有些是罗马数字,有些是专有名词(France、London、Shakespeare),还有一些是名副其实的罕见词或虚构词(seaography、gryphon)。不过,还是有一些词看起来不太合适,比如smallest、larger、loveliest ——比较级和最高级。我在 NodeBox 中没有找到任何可以帮助将这些词简化为基本形容词或副词的工具。还有一些动词,比如dreamed,没有正确转换为不定式。
我注意到自然语言工具包 (NLTK) 中有一个工具可以做到这一点 -获取单词的基本(或“词干”)形式。
我尝试了两种不同的“词干提取器”,Porter 词干提取器和 Snowball 词干提取器:
def unusual_words_porter(text):
text_vocab = set(w.lower() for w in text if w.isalpha())
english_vocab = set(w.lower() for w in nltk.corpus.words.words())
unusual = text_vocab.difference(english_vocab)
basic_forms = set(basic_word_form_porter(w) for w in unusual)
return sorted(basic_forms.difference(english_vocab))
def basic_word_form_porter(word):
word = word.lower()
stemmer = nltk.PorterStemmer()
return stemmer.stem(word)
def unusual_words_snowball(text):
text_vocab = set(w.lower() for w in text if w.isalpha())
english_vocab = set(w.lower() for w in nltk.corpus.words.words())
unusual = text_vocab.difference(english_vocab)
basic_forms = set(basic_word_form_snowball(w) for w in unusual)
return sorted(basic_forms.difference(english_vocab))
def basic_word_form_snowball(word):
word = word.lower()
stemmer = nltk.SnowballStemmer('english')
return stemmer.stem(word)
这些分别返回了 187 个和 188 个单词的列表。词干提取器似乎并没有完全满足我的需要——它们经常会去掉动词变位或不规则复数,以至于剩下的不是一个完整的英语单词。以下是一些输出:trembl、turtl、difficulti。
我又发现了一个工具:lemmatizer
。它的目的是把单词简化成它们的“词干”,即“一个词的规范、词典或引用形式”。听起来很有希望。
>>> lemmatizer = WordNetLemmatizer()
>>> lemmatizer.lemmatize('hedgehogs')
u'hedgehog'
>>> lemmatizer.lemmatize('says')
u'say'
我注意到,如果通过可选的第二个参数指定单词的词性,效果会更好。
>>> lemmatizer.lemmatize('prettier')
'prettier'
>>> lemmatizer.lemmatize('smallest')
'smallest'
>>> lemmatizer.lemmatize('smallest', pos='a')
u'small'
>>> lemmatizer.lemmatize('prettier', pos='a')
u'pretty'
问题是,我不知道这些词的词性,有些词性可能根据上下文而不同,而我又没有上下文。所以我尝试了每个词,先不带词性,然后是形容词、动词和名词,最后返回了第一个与起始词不匹配的词。🤷
def basic_word_form(word):
word = word.lower()
lemmatized_forms = [lemmatizer.lemmatize(word), lemmatizer.lemmatize(word, pos='a'), lemmatizer.lemmatize(word, pos='v'), lemmatizer.lemmatize(word, pos='n')]
for form in lemmatized_forms:
if form != word:
return form
return word
这产生了迄今为止最短的单词列表,只有 71 个单词。主要的误报似乎是英式拼写,例如skurried、neighborhood、curtsey、罗马数字,以及原始语料库中莫名其妙缺失的真实单词,例如kid、proud。但其中很多词确实看起来非常不寻常。
我决定做最后一个实验,用同样的方法处理《爱丽丝镜中奇遇记》的文本。我以为会发现更多自创词,包括《Jabberwocky》里著名的vorpal、uffish、brillig和slithy 。
默认情况下,此文本未包含在 NLTK 中,因此我从古腾堡计划中获取了它。
当我运行它时,我得到了 87 个不常见的单词,包括brillig、callay、callooh、frumious等。有趣的是,vorpal和slithy似乎已经进入了 NLTK“真实”词汇库,所以它们没有被返回。😂
这是我第一次尝试自然语言处理工具。这显然不是最有效的方法,所以我很想找到其他可行的方法。如果您有任何疑问或想法,请告诉我您的想法!
鏂囩珷鏉ユ簮锛�https://dev.to/valeriecodes/exploring-natural-language-processing-with-alice-in-wonderland-ldc