二分类情感分析

Admin
发布于 2026-05-30 / 0 阅读
0
0
https://storage.googleapis.com/learning-datasets/binary-emotion.csv
from bs4 import BeautifulSoup
import string

stopwords = ["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "as", "at","be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "could", "did", "do",
"does", "doing", "down", "during", "each", "few", "for", "from", "further", "had", "has", "have", "having",
"he", "hed", "hes", "her", "here", "heres", "hers", "herself", "him", "himself", "his", "how",
"hows", "i", "id", "ill", "im", "ive", "if", "in", "into", "is", "it", "its", "itself",
"lets", "me", "more", "most", "my", "myself", "nor", "of", "on", "once", "only", "or", "other", "ought",
"our", "ours", "ourselves", "out", "over", "own", "same", "she", "shed", "shell", "shes", "should",
"so", "some", "such", "than", "that", "thats", "the", "their", "theirs", "them", "themselves", "then",
"there", "theres", "these", "they", "theyd", "theyll", "theyre", "theyve", "this", "those", "through",
"to", "too", "under", "until", "up", "very", "was", "we", "wed", "well", "were", "weve", "were",
"what", "whats", "when", "whens", "where", "wheres", "which", "while", "who", "whos", "whom", "why",
"whys", "with", "would", "you", "youd", "youll", "youre", "youve", "your", "yours", "yourself","yourselves"]

table = str.maketrans('', '', string.punctuation)

下面我将为你详细解释这段Python代码的含义、实现原理、用途以及注意事项。

### 一、 代码整体含义

这段代码主要完成了两个准备工作:

1. 定义了一个包含大量英语停用词的列表 stopwords

2. 创建了一个用于删除字符串中所有标点符号的字符映射转换表 table

这通常是**自然语言处理(NLP)**或**文本数据预处理**阶段的前两步核心代码,常见于文本分类、情感分析、词频统计等任务之前。

---

### 二、 分段详细解释

#### 1. stopwords 列表

```python

stopwords = ["a", "about", "above", ...]

```

* 含义:这是一个包含了常见英语停用词的列表。

* 什么是停用词:停用词是指在文本中频繁出现,但对文本核心语义贡献极小的词汇。例如 "a", "the", "is", "and" 等。它们主要起语法连接作用,本身没有太多实际意义。

* 用途:在进行文本分析时,这些词会引入大量噪音,增加计算量且干扰模型提取关键特征。因此,通常会在预处理阶段将它们过滤掉。

#### 2. table = str.maketrans('', '', string.punctuation)

这行代码非常精妙,它利用了Python内置字符串方法 maketrans 来构建一个翻译/映射表。

* *string.punctuation**:这是 string 模块中的常量,包含了所有的英文标点符号,值为 '!"#$%&\'()*+,-./:;<=>?@[\\]^_{|}~'`。

* *str.maketrans(x, y, z)**:当传入三个参数时:

* 第一个参数 x:要替换的字符(这里为空字符串 '')。

* 第二个参数 y:替换成的字符(这里为空字符串 '',注意 xy 长度必须一致,因为它们是一一对应的,这里长度都是0)。

* 第三个参数 z:要删除的字符。

* 实现原理:这行代码生成了一个字符映射字典(Translation Table),该字典指定了:将 string.punctuation 中的每一个标点符号映射为 None(即删除)。

* 用途:配合 str.translate() 方法,可以极速、批量地清除文本中的所有标点符号。

---

### 三、 实际应用场景(如何使用)

这段代码只是定义了工具,通常它们会配合以下方式使用:

```python

import string

from bs4 import BeautifulSoup

# ...(此处为你的代码:定义 stopwords 和 table)...

# 假设我们获取到了一段带有HTML标签和标点符号的脏文本

raw_html = "<p>Hello, world! This is a test for NLP preprocessing.</p>"

# 1. 使用 BeautifulSoup 去除 HTML 标签,提取纯文本

soup = BeautifulSoup(raw_html, 'html.parser')

clean_text = soup.get_text()

# 结果: "Hello, world! This is a test for NLP preprocessing."

# 2. 使用 table 去除标点符号

no_punct_text = clean_text.translate(table)

# 结果: "Hello world This is a test for NLP preprocessing"

# 3. 转换为小写并按空格切分

words = no_punct_text.lower().split()

# 结果: ['hello', 'world', 'this', 'is', 'a', 'test', 'for', 'nlp', 'preprocessing']

# 4. 过滤停用词

final_words = [word for word in words if word not in stopwords]

# 结果: ['hello', 'world', 'test', 'nlp', 'preprocessing']

```

注:代码中引入了 BeautifulSoup,说明这段代码的上下文很可能是用于解析网页抓取下来的数据,先用 BeautifulSoup 剥离 HTML 标签,再进行上述的文本清洗。

---

### 四、 注意事项与优化建议

1. 性能优化(重要)

在过滤停用词时,代码 if word not in stopwords 中的 stopwords 是一个**列表**。在Python中,列表的 in 操作时间复杂度是 $O(N)$。如果停用词很多或文本很大,这会非常慢。

建议:将 stopwords 转换为**集合**,集合的 in 操作时间复杂度是 $O(1)$,查找速度极快。

```python

stopwords_set = set(stopwords) # 初始化时转为集合

final_words = [word for word in words if word not in stopwords_set]

```

2. 停用词的适用性

代码中的停用词表是标准的英文停用词。如果你处理的是中文、其他语言,或者特定领域的文本(如医学、法律),这个列表是不够的,需要根据业务场景自定义停用词表。

3. 标点符号处理的边界情况

string.punctuation 只包含标准的 ASCII 英文标点符号。如果你的文本中包含中文标点(如 ,。!?)、省略号 、特殊的 Unicode 引号(如 “”),它们将无法被 table 删除。对于多语言文本,可能需要使用正则表达式(如 re.sub(r'[^\w\s]', '', text))来移除所有非单词非空白字符。

4. 大小写问题

在比对停用词之前,务必确保文本已经被转换为**小写**.lower())。因为停用词表里都是小写,如果文本是 "The",它就不会被识别为停用词而被错误保留。

import csv
sentences=[]
labels=[]
with open('binary-emotion.csv', encoding='UTF-8') as csvfile:
    reader = csv.reader(csvfile, delimiter=",")
    for row in reader:
        labels.append(int(row[0]))
        sentence = row[1].lower()
        sentence = sentence.replace(",", " , ")
        sentence = sentence.replace(".", " . ")
        sentence = sentence.replace("-", " - ")
        sentence = sentence.replace("/", " / ")
        soup = BeautifulSoup(sentence)
        sentence = soup.get_text()
        words = sentence.split()
        filtered_sentence = ""
        for word in words:
            word = word.translate(table)
            if word not in stopwords:
                filtered_sentence = filtered_sentence + word + " "
        sentences.append(filtered_sentence)

下面我将为你详细解释这段Python代码的含义、实现原理、用途以及需要注意的事项。

### 一、 代码含义与用途

这段代码的主要用途是**自然语言处理(NLP)的数据预处理**。它从一个名为 binary-emotion.csv 的CSV文件中读取文本和标签数据,对文本进行一系列的清洗和标准化操作(如大小写转换、标点符号处理、去除HTML标签、去除停用词等),最终将处理后的干净文本和对应的情感标签分别存储在 sentenceslabels 两个列表中。

从文件名 binary-emotion.csv 和标签提取方式来看,是一个**二分类情感分析**(如正面/负面,或积极/消极)的数据集准备阶段。

### 二、 实现原理(逐行解析)

1. *import csv**

导入Python内置的CSV模块,用于读取逗号分隔值的文件。

2. *sentences=[] / labels=[]**

初始化两个空列表,用于分别存放预处理后的句子文本和对应的情感标签。

3. *with open('binary-emotion.csv', encoding='UTF-8') as csvfile:**

以只读模式打开当前目录下的 binary-emotion.csv 文件,指定编码为UTF-8,使用 with 语句确保操作完成后文件会自动关闭。

4. *reader = csv.reader(csvfile, delimiter=",")**

创建一个CSV读取器,指定列与列之间的分隔符为逗号 ,

5. *for row in reader:**

遍历CSV文件中的每一行数据,每一行 row 是一个列表,包含该行的各列内容。

6. *labels.append(int(row[0]))**

将每行的第一列(索引0)转换为整数类型,并添加到 labels 列表中。这通常是文本的分类标签(如0代表消极,1代表积极)。

7. *sentence = row[1].lower()**

提取每行的第二列(索引1)作为句子文本,并使用 .lower() 将所有字符转换为小写。这是NLP的常见操作,可以减少词汇量(使"Apple"和"apple"被视为同一个词)。

8. 标点符号两边加空格:

```python

sentence = sentence.replace(",", " , ")

sentence = sentence.replace(".", " . ")

sentence = sentence.replace("-", " - ")

sentence = sentence.replace("/", " / ")

```

将特定的标点符号(逗号、句号、连字符、斜杠)替换为“空格+标点+空格”。**原理**:在后续使用 split() 切分句子时,如果标点紧贴着单词(如"apple,"),它们会被切分成一个整体["apple,"];加上空格后,标点就会被切分成独立的元素["apple", ","],这有助于在词汇层面对标点进行单独处理或过滤。

9. *soup = BeautifulSoup(sentence) / sentence = soup.get_text()**

使用 BeautifulSoup 库解析句子,并提取纯文本。**原理**:原始文本中可能包含HTML标签(如 <br>, <p> 等)get_text() 方法可以剥离所有HTML标签,只保留文字内容。

10. *words = sentence.split()**

使用空格将句子切分成单词列表。

11. 过滤停用词与特殊字符:

```python

filtered_sentence = ""

for word in words:

word = word.translate(table)

if word not in stopwords:

filtered_sentence = filtered_sentence + word + " "

```

- word.translate(table):使用字符串的 translate 方法,根据映射表 table(通常用于删除或替换特定字符,如去除特殊标点或数字)处理单词。

- if word not in stopwords:检查处理后的单词是否在停用词表 stopwords 中(停用词如 "the", "a", "is" 等,对情感判断无实际帮助且高频出现)。

- 如果单词不在停用词表中,则将其拼接到 filtered_sentence 中,单词间用空格分隔。

12. *sentences.append(filtered_sentence)**

将最终清洗完毕的句子添加到 sentences 列表中。

### 三、 注意事项与潜在问题

1. 缺失的导入和变量定义

代码中使用了 BeautifulSouptablestopwords,但在代码片段中并未定义。在实际运行前,必须补充:

- 导入 BeautifulSoupfrom bs4 import BeautifulSoup

- 定义 table:通常使用 str.maketrans('', '', string.punctuation) 来去除所有标点。

- 定义 stopwords:通常是一个包含常见停用词的集合set),如 stopwords = set(['the', 'a', 'an', ...])

2. 字符串拼接效率低

在循环中使用 filtered_sentence = filtered_sentence + word + " " 进行字符串拼接,在Python中由于字符串是不可变对象,每次拼接都会创建新对象,当数据量很大时效率极低。

优化建议:使用列表的 append 方法,最后用 " ".join() 合并:

```python

filtered_words = []

for word in words:

word = word.translate(table)

if word not in stopwords:

filtered_words.append(word)

filtered_sentence = " ".join(filtered_words)

```

3. 表头处理缺失

如果CSV文件的第一行是表头(如 "label, sentence"),代码会将表头也作为数据处理int(row[0]) 会报错)。建议使用 next(reader) 跳过表头,或者使用 csv.DictReader

4. 标点处理的逻辑冲突

代码前半部分特意给逗号、句号等加了空格,使其成为独立的 token。但后半部分又使用了 word.translate(table)(通常table是用来消除标点的)和停用词过滤。如果 table 删除了所有标点,那么前面加空格的步骤就变成了无用功;如果目的是保留标点作为特征,那么 table 的映射规则需要谨慎编写。

5. 缺少异常处理

如果某一行数据格式不正确(例如只有一列,导致 row[1] 抛出 IndexError),或者标签不是数字(导致 int(row[0]) 抛出 ValueError),程序会直接崩溃。建议添加 try-except 块来跳过或记录异常数据。

如果你有关于这段代码更具体的疑问,或者需要补充完整的预处理代码,随时可以告诉我!

from collections import Counter
from bs4 import BeautifulSoup

# 假设停用词列表已定义
stopwords = ["a", "an", "the", "is", "in", "and", "of", "to", "it"] # 示例截断

# Tokenize 函数保持不变
def tokenize(text):
    soup = BeautifulSoup(text, "html.parser")
    cleaned_text = soup.get_text()
    tokens = cleaned_text.lower().split()
    filtered_tokens = [token for token in tokens if token not in stopwords]
    return filtered_tokens

def build_vocab(sentences):
    counter = Counter()
    for text in sentences:
        counter.update(tokenize(text))

    sorted_words = sorted(counter.items(), key=lambda x: x[1], reverse=True)

    vocab = {word: idx + 1 for idx, (word, _) in enumerate(sorted_words)}
    vocab['<pad>'] = 0
    return vocab, sorted_words # 🔥 修改:同时返回排序好的词频列表,方便后续打印

# 假设这是你的训练数据
training_sentences = [
    "This is a great movie!",
    "This film is terrible and boring.",
    "I love this movie, it is great."
]

# 🔥 修改:接收返回的排序列表
vocab, sorted_words = build_vocab(training_sentences)

# ==========================================
# 美观打印区域
# ==========================================

print(f"📊 词汇表总大小: {len(vocab)} 个词\n")

# -----------
# 方式一:查看高频词和低频词(最推荐,快速了解数据分布)
# -----------
print("🏆 【Top 10 高频词】(索引越小,频次越高):")
print("-" * 35)
for word, freq in sorted_words[:10]:
    idx = vocab[word]
    # 使用 f-string 格式化对齐:{idx:<5} 表示左对齐占5个字符宽度
    print(f"  索引: {idx:<5} 词频: {freq:<5} 词: '{word}'")

print("\n...\n")

print("📉 【Top 10 低频词】(出现次数最少的词):")
print("-" * 35)
for word, freq in sorted_words[-10:]:
    idx = vocab[word]
    print(f"  索引: {idx:<5} 词频: {freq:<5} 词: '{word}'")


# -----------
# 方式二:分段打印(适合查看特定范围的索引映射,像字典一样)
# -----------
print("\n📖 【索引 1~10 映射详情】(每行3个):")
print("-" * 50)
# 取出索引 1 到 10 的词 (跳过0的<pad>)
items_to_show = list(vocab.items())[1:11] 
for i in range(0, len(items_to_show), 3):
    chunk = items_to_show[i:i+3]
    # 生成类似 " 'great': 1  |  'movie': 2  |  'terrible': 3 " 的格式
    line = "  |  ".join([f"'{word}': {idx}" for word, idx in chunk])
    print(f"  {line}")


# -----------
# 方式三:查找特定词汇的索引(最实用的调试方法)
# -----------
test_words = ['great', 'movie', 'boring', 'awesome', '<pad>']
print("\n🔍 【特定词汇查询】:")
print("-" * 30)
for word in test_words:
    # 使用 get 方法,如果找不到返回 '未收录' 标识
    idx = vocab.get(word, "未收录(UNK)")
    print(f"  '{word}' -> 索引: {idx}")

这段Python代码的主要功能是**构建自然语言处理(NLP)任务中的词汇表,并以多种美观的方式对词汇表进行统计和打印展示**。

下面我将从实现原理、用途和注意事项三个方面为您详细解释:

### 一、 实现原理

代码的整体执行流程分为三个阶段:文本预处理与分词、词频统计与词汇表构建、格式化打印。

1. **文本预处理与分词 tokenize 函数)**:

- **去除HTML标签**:使用 BeautifulSoup 将可能含有HTML标签的文本解析为纯文本。

- **统一小写**:将文本全部转为小写,避免 "Great" 和 "great" 被识别为两个不同的词。

- **简单分词**:使用 Python 内置的 split() 方法按空格切分文本。

- **去除停用词**:通过列表推导式过滤掉 "a", "is", "in" 等对文本语义贡献不大但频率极高的词。

2. **构建词汇表 build_vocab 函数)**:

- **词频统计**:使用 collections.Counter 遍历所有句子,统计每个词的出现次数。

- **按频排序**:将统计结果按词频从高到低排序。高频词排在前面,这在NLP中很常见,可以使得常用词的索引较小,有助于模型收敛和嵌入层压缩。

- **生成映射字典**:通过字典推导式,为排序后的词分配从 1 开始的递增整数索引。

- **添加占位符**:将 <pad>(填充符)的索引硬编码为 0。在批处理训练时,不同长度的句子需要补齐到相同长度0 通常作为填充位置的索引。

3. **美观打印展示**:

- **方式一(高低频极值查看)**:通过切片 [:10][-10:] 快速查看数据分布的头部和尾部,使用 f-string:<5 语法实现左对齐和固定宽度,使输出像表格一样整齐。

- **方式二(分段映射查看)**:将字典转为列表后切片,每次循环步长为3,实现每行打印3个键值对的排版。

- **方式三(特定词汇查询)**:使用字典的 get(key, default) 方法安全查询,避免词汇不存在时抛出 KeyError 异常。

---

### 二、 用途

1. **NLP模型的数据预处理基础**:在构建文本分类、情感分析、机器翻译等模型前,必须将文本转换为数字。这段代码生成的 vocab 字典就是“词到索引”的映射表,是后续词嵌入(Word Embedding)层的基石。

2. **数据探索与质量检查(EDA)**:通过查看高频词和低频词,可以快速发现数据集的特点或问题。例如:如果高频词中包含大量无意义词汇,说明停用词表不够完善;如果低频词全是错别字,则可能需要进行拼写纠正或字符级过滤。

3. **调试与日志记录**:在训练模型时,打印特定词汇的索引或局部映射,有助于开发者验证数据管道是否正确,确认 <pad> 等特殊标记是否被正确处理。

---

### 三、 注意事项

1. **分词方式的局限性**:

- 代码使用的是 str.split(),这只能按空格切分。对于英文,它无法处理标点符号(如 "movie!" 会被切分为 "movie!" 而不是 "movie""!");对于中文等非空格分隔的语言,此方法完全失效。**改进建议**:使用 nltk.word_tokenizespaCy 等专业分词工具。

2. **停用词表过于简单**:

- 示例中的 stopwords 仅有9个词,实际应用中需要更完整的停用词表。同时,标点符号(如 !, ,, .)并没有在过滤逻辑中被剔除,它们会被当作普通词计入词汇表。

3. **未处理未知词**:

- 词汇表只包含了训练集里出现过的词。在测试或推理阶段,如果遇到新词,代码中用 "未收录(UNK)" 字符串代替了索引。但在实际送入模型时,必须为其分配一个特定的整数索引(通常为 1,即 <unk> 标记)。**改进建议**:在 build_vocab 中添加 vocab['<unk>'] = 1,并将其他词的索引顺延,查询时使用 vocab.get(word, 1)

4. **字典遍历顺序的依赖**:

- 在“方式二”的代码 list(vocab.items())[1:11] 中,默认了字典遍历顺序与插入顺序一致。这在 Python 3.7+ 中是成立的(字典保持插入顺序),但如果在更老的 Python 版本中运行,输出顺序将是随机的。

5. **低频词切片的边界情况**:

- 当词汇表总大小小于10时sorted_words[:10]sorted_words[-10:] 会有重叠,打印时可能会看到相同的词出现在高频和低频区域。


评论