针对大规模乌尔都语新闻数据集:如何快速遍历pandas DataFrame指定列并完成NLTK分词以构建n-gram语言模型?
优化乌尔都语新闻语料分词与N-gram模型的性能与逻辑
一、核心性能优化:替换低效的For循环分词
你的核心问题是用纯Python循环遍历11万行数据分词,速度极慢。这里有几个层级的优化方案,从简单到进阶:
1. 用Pandas向量化操作替代循环
Pandas的apply方法内部做了底层优化,比手动range循环高效得多。可以直接对"News Text"列批量分词,再通过itertools.chain高效展平为一维tokens列表:
import pandas as pd import nltk from itertools import chain # 读取并预处理数据 dataframe = pd.read_excel("urdu-news-dataset-1M.xlsx").drop(["Index"], axis=1) # 批量分词:先转字符串避免空值报错,再对每行分词,最后展平结果 tokens = list(chain.from_iterable( dataframe["News Text"].astype(str).apply(nltk.word_tokenize) ))
itertools.chain.from_iterable比循环里的tokens+=更高效,它避免了列表重复扩容带来的性能损耗。
2. 并行加速分词(进阶)
如果数据量特别大,可以利用多核CPU并行处理分词任务。swifter库会自动判断最优的执行方式(向量化/并行),用起来非常省心:
import swifter # 用swifter加速分词操作 token_series = dataframe["News Text"].astype(str).swifter.apply(nltk.word_tokenize) tokens = list(chain.from_iterable(token_series))
安装swifter:pip install swifter
3. 换用乌尔都语专用分词器(可选)
NLTK的word_tokenize对乌尔都语的支持不算最优,你可以试试urduhack库的专用分词器,它针对乌尔都语的语法和字符做了优化,准确率和速度都会更好:
from urduhack import tokenization # 乌尔都语专用分词函数 def urdu_tokenize(text): return tokenization.word_tokenize(text) tokens = list(chain.from_iterable( dataframe["News Text"].astype(str).apply(urdu_tokenize) ))
安装urduhack:pip install urduhack(部分环境可能需要额外配置依赖)
二、修正N-gram模型的逻辑错误
你的示例输出出现了字符连续拼接的问题,这是因为代码里的预处理循环逻辑完全错误:把单个词拆成了字符处理。下面是修正后的完整逻辑:
修正后的完整代码
import pandas as pd import nltk from itertools import chain from nltk.probability import FreqDist from collections import defaultdict, Counter import random # ---------------------- 数据读取与分词优化 ---------------------- dataframe = pd.read_excel("urdu-news-dataset-1M.xlsx").drop(["Index"], axis=1) # 批量分词并展平 tokens = list(chain.from_iterable( dataframe["News Text"].astype(str).apply(nltk.word_tokenize) )) # ---------------------- 预处理:转小写、过滤标点 ---------------------- # 加入乌尔都语常用标点,可根据需求扩展 punctuations = {'.', '!', '?', ',', '،', '۔', ':', ';'} processed_tokens = [ token.lower() for token in tokens if token not in punctuations and token.strip() != "" ] # ---------------------- 生成N-gram ---------------------- unigram = processed_tokens bigram = list(nltk.ngrams(processed_tokens, 2, pad_left=True, pad_right=True)) trigram = list(nltk.ngrams(processed_tokens, 3, pad_left=True, pad_right=True)) quadgram = list(nltk.ngrams(processed_tokens, 4, pad_left=True, pad_right=True)) quingram = list(nltk.ngrams(processed_tokens, 5, pad_left=True, pad_right=True)) # ---------------------- 计算频率与构建模型 ---------------------- frequency_bigram = FreqDist(bigram) frequency_trigram = FreqDist(trigram) frequency_quadgram = FreqDist(quadgram) frequency_quingram = FreqDist(quingram) bigram_model = defaultdict(Counter) trigram_model = defaultdict(Counter) quadgram_model = defaultdict(Counter) quingram_model = defaultdict(Counter) for i, j in frequency_bigram: if i is not None and j is not None: bigram_model[i][j] += frequency_bigram[(i,j)] for i, j, k in frequency_trigram: if i is not None and j is not None and k is not None: trigram_model[(i,j)][k] += frequency_trigram[(i,j,k)] for i, j, k, l in frequency_quadgram: if i is not None and j is not None and k is not None and l is not None: quadgram_model[(i,j,k)][l] += frequency_quadgram[(i,j,k,l)] for i, j, k, l, m in quingram: if i is not None and j is not None and k is not None and l is not None and m is not None: quingram_model[(i,j,k,l)][m] += frequency_quingram[(i,j,k,l,m)] # ---------------------- 文本生成函数 ---------------------- def predict_word(count): return random.choice(list(count.elements())) # 注意输入应为完整的乌尔都语词,而非单个字符 input_words = ("ق", "ب") sentence = " ".join(input_words) # 用词间空格分隔,避免字符拼接 print("初始输入:", sentence) print("生成过程:") for _ in range(200): try: suffix = predict_word(trigram_model[input_words]) sentence += " " + suffix print(sentence) input_words = (input_words[1], suffix) except KeyError: # 无匹配N-gram时随机选词继续 suffix = random.choice(processed_tokens) sentence += " " + suffix input_words = (input_words[1], suffix) print("\n最终生成的文章:") print(sentence)
三、额外性能优化建议
- 分块处理大文件:如果Excel文件过大,可分块读取处理,避免内存溢出:
# 分块读取,每块10000行 chunk_iter = pd.read_excel("urdu-news-dataset-1M.xlsx", chunksize=10000) tokens = [] for chunk in chunk_iter: chunk = chunk.drop(["Index"], axis=1) tokens.extend(chain.from_iterable(chunk["News Text"].astype(str).apply(nltk.word_tokenize)))
- 缓存分词结果:如果需要多次运行模型,可把分词结果保存为pickle文件,避免重复分词:
import pickle # 保存分词结果 with open("urdu_tokens.pkl", "wb") as f: pickle.dump(processed_tokens, f) # 读取缓存的分词结果 with open("urdu_tokens.pkl", "rb") as f: processed_tokens = pickle.load(f)
- 直接构建N-gram模型:如果不需要保存所有N-gram列表,可直接遍历分词结果构建模型,节省内存:
# 直接构建trigram模型,无需先生成trigram列表 trigram_model = defaultdict(Counter) for i in range(len(processed_tokens)-2): trigram_key = (processed_tokens[i], processed_tokens[i+1]) trigram_model[trigram_key][processed_tokens[i+2]] += 1
内容的提问来源于stack exchange,提问作者Huzaifa Khan
相关产品推荐
相关产品推荐

