和《爱丽丝梦游仙境》一起探索自然语言处理

2025-06-10

和《爱丽丝梦游仙境》一起探索自然语言处理

作为一个编程迷和文学迷🤓,我最近一直在尝试寻找更多方法将两者结合起来。我目前正在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 个单词。这看起来太多了,所以我浏览了其中一些。其中很多单词看起来都很常见:eggsgrinshappenspresented

好吧,这也许比我想象的要难。我有点沮丧,于是尝试使用其他语料库。但这个语料库有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 个单词的列表。还不错。有些是罗马数字,有些是专有名词(FranceLondonShakespeare),还有一些是名副其实的罕见词或虚构词(seaographygryphon)。不过,还是有一些词看起来不太合适,比如smallestlargerloveliest ——比较级和最高级。我在 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 个单词的列表。词干提取器似乎并没有完全满足我的需要——它们经常会去掉动词变位或不规则复数,以至于剩下的不是一个完整的英语单词。以下是一些输出:tremblturtldifficulti

我又发现了一个工具: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 个单词。主要的误报似乎是英式拼写,例如skurriedneighborhoodcurtsey、罗马数字,以及原始语料库中莫名其妙缺失的真实单词,例如kidproud。但其中很多词确实看起来非常不寻常。

我决定做最后一个实验,用同样的方法处理《爱丽丝镜中奇遇记》的文本。我以为会发现更多自创词,包括《Jabberwocky》里著名的vorpaluffishbrilligslithy 。

默认情况下,此文本未包含在 NLTK 中,因此我从古腾堡计划中获取了它。

当我运行它时,我得到了 87 个不常见的单词,包括brilligcallaycalloohfrumious等。有趣的是,vorpalslithy似乎已经进入了 NLTK“真实”词汇库,所以它们没有被返回。😂

这是我第一次尝试自然语言处理工具。这显然不是最有效的方法,所以我很想找到其他可行的方法。如果您有任何疑问或想法,请告诉我您的想法!

鏂囩珷鏉ユ簮锛�https://dev.to/valeriecodes/exploring-natural-language-processing-with-alice-in-wonderland-ldc
PREV
为你的下一个设计项目挑选合适的照片
NEXT
一劳永逸:JavaScript 中的 const 不是不可变的