🌟 完整项目和代码

本教程是 AI 入门 30 天挑战 系列的一部分!


Week 4 第三天:AI 如何读懂人心!
BERT 情感分析 + 股票论坛情绪监控!
每个概念都解释!每行代码都说明白!
预计时间:3-4 小时(含费曼输出练习)


📖 第 1 步:快速复习昨天的内容(30 分钟)

费曼输出 #0:考考你

合上教程,尝试回答:

□ GPT 的"自回归"是什么意思?用例子说明
□ 为什么 GPT 只能用 Masked Attention?
□ 温度系数 (temperature) 高低各有什么影响?
□ GPT 和 BERT 的核心区别是什么?
□ 如果用情感分析监控股民情绪,你会怎么设计?

⏰ 时间:25 分钟

如果能答出 80% 以上,我们开始今天的情感分析之旅!如果不够,花 5 分钟翻一下 Day23 的笔记。


🤔 第 2 步:情感分析到底是什么?(60 分钟)

说人话版本

想象你在看股票论坛:

场景 1:你看了一条评论
"这个公司业绩太好了,强烈推荐买入!"

你的反应:
→ 这是正面评价(利好)
→ 你可能也会想买

场景 2:又看到一条
"完了完了,股价要崩盘了,赶紧跑!"

你的反应:
→ 这是负面评价(利空)
→ 你可能会想卖

这就是情感分析!
- 输入:文字评论
- 输出:正面/负面(利好/利空)
- 应用:舆情监控、投资辅助
生活中的例子:察言观色

你和朋友聊天:
朋友笑着说:"太棒了!" → 开心(正面)
朋友皱着眉说:"真糟糕。" → 难过(负面)

情感分析就是教 AI"察言观色":
- 看用词("太棒了"vs"真糟糕")
- 看语气(感叹号、问号)
- 看上下文(前后文的意思)

然后判断:这是高兴还是生气?
是利好还是利空?

情感分析的层次

Level 1: 二元分类(最简单)
✓ 正面 vs 负面
✓ 利好 vs 利空
✓ 开心 vs 生气

Level 2: 多元分类
✓ 非常正面、正面、中性、负面、非常负面
✓ 5 星评分(1-5 星)

Level 3: 细粒度情感
✓ 对各个方面的评价
"这个手机屏幕很好,但电池不行"
→ 屏幕:正面
→ 电池:负面

Level 4: 复杂情感
✓ 喜忧参半
✓ 爱恨交织
✓ 期待又担心

🎯 费曼输出 #1:向小白解释情感分析

任务 1:创造多个比喻

场景 A:向小学生解释

用表情符号
正面 = 😊 笑脸
负面 = 😠 哭脸
AI 的工作:读文字 → 画表情

比如:
"今天好开心" → 😊
"气死我了" → 😠
"一般般" → 😐

场景 B:向股民解释

用看盘软件
红色 = 涨(利好)
绿色 = 跌(利空)
AI 的工作:读新闻 → 标颜色

比如:
"业绩大增" → 🔴 利好
"利润下滑" → 🟢 利空
"平稳发展" → ⚪ 中性

场景 C:向编辑解释

用审稿标记
红笔圈出好词 ✅
蓝笔圈出坏词 ❌
统计哪种多

比如:
"优秀、卓越、出色" → 好词多 → 正面
"糟糕、失败、垃圾" → 坏词多 → 负面

要求: 每个场景都要详细说明

⏰ 时间:20 分钟


💡 卡壳检查点

如果你在解释时卡住了:

□ 我说不清楚 AI 是怎么判断情感的
□ 我不知道如何解释"训练"的过程
□ 我只能说"AI 识别",但不能说明白怎么识别

这很正常! 标记下来,继续往下看,然后重新尝试解释!

提示:

  • 训练 = 给 AI 看很多例子(带标签的评论)
  • 学习 = AI 找规律(什么词对应什么情感)
  • 预测 = 用学到的规律判断新评论

🔬 第 3 步:BERT 情感分析详解(70 分钟)

核心思想

传统方法(关键词匹配):
看到"好"→正面
看到"差"→负面

问题:
"这个产品不好" → 有"好"字 → 错判为正面❌
"这个产品不差" → 有"差"字 → 错判为负面❌

BERT 的方法(理解上下文):
看完整个句子再判断
"这个产品不好" → 否定句 → 负面✅
"这个产品不差" → 双重否定 → 正面✅

优势:
✓ 理解语境
✓ 处理否定
✓ 捕捉细微差别

完整代码实现

import torch
from transformers import BertTokenizer, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
import numpy as np
import matplotlib.pyplot as plt

print("=" * 60)
print("📊 BERT 情感分析系统 - 股票评论监控")
print("=" * 60)

# ============================================================================
# 第一部分:准备数据
# ============================================================================
print("\n【1. 准备股票评论数据】")

# 示例数据(真实场景应该收集更多)
stock_comments = [
    # 正面评论(标签 1)
    ("这个公司业绩太好了,强烈推荐买入!", 1),
    ("财报超预期,股价肯定要涨", 1),
    ("新产品很有竞争力,看好未来", 1),
    ("管理层很给力,战略清晰", 1),
    ("行业龙头,护城河深", 1),
    ("估值合理,值得长期持有", 1),
    ("技术领先,市场前景广阔", 1),
    ("盈利能力强劲,分红大方", 1),
    ("订单爆满,产能跟不上", 1),
    ("分析师上调目标价,信心十足", 1),
    
    # 负面评论(标签 0)
    ("这个公司要完蛋了,赶紧跑!", 0),
    ("财报暴雷,利润大幅下滑", 0),
    ("产品没有竞争力,被淘汰了", 0),
    ("管理层乱来,战略不明", 0),
    ("行业衰退,前景堪忧", 0),
    ("估值太高,泡沫严重", 0),
    ("技术落后,被对手超越", 0),
    ("连年亏损,快要破产了", 0),
    ("订单稀少,产能过剩", 0),
    ("分析师下调评级,建议卖出", 0),
    
    # 中性评论(标签 2)
    ("今天股价波动不大,正常", 2),
    ("消息面平静,观望为主", 2),
    ("业绩符合预期,无功无过", 2),
    ("市场反应平淡,没什么亮点", 2),
    ("维持原有评级,不做调整", 2),
]

print(f"✓ 数据集大小:{len(stock_comments)} 条评论")

# 统计各类别数量
from collections import Counter
labels = [label for _, label in stock_comments]
label_counts = Counter(labels)

print(f"  - 正面评论:{label_counts[1]} 条")
print(f"  - 负面评论:{label_counts[0]} 条")
print(f"  - 中性评论:{label_counts[2]} 条")

# 分割训练集和测试集
train_data, test_data = train_test_split(stock_comments, test_size=0.3, random_state=42)

print(f"\n  - 训练集:{len(train_data)} 条")
print(f"  - 测试集:{len(test_data)} 条")

# ============================================================================
# 第二部分:加载预训练 BERT 模型
# ============================================================================
print("\n" + "=" * 60)
print("【2. 加载 BERT 模型】")
print("=" * 60)

print("正在加载中文 BERT 模型...")
print("提示:第一次会自动下载,请耐心等待")

tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained(
    'bert-base-chinese',
    num_labels=3  # 3 类:正面、负面、中性
)

print(f"✓ BERT 模型加载完成")
print(f"  隐藏层维度:{model.config.hidden_size}")
print(f"  分类类别数:{model.config.num_labels}")

# ============================================================================
# 第三部分:数据预处理
# ============================================================================
print("\n" + "=" * 60)
print("【3. 数据预处理】")
print("=" * 60)

def preprocess_data(data_list, tokenizer, max_length=128):
    """将文本转换为 BERT 可以接受的格式"""
    
    encodings = []
    labels = []
    
    for text, label in data_list:
        # 分词和编码
        encoding = tokenizer.encode_plus(
            text,
            add_special_tokens=True,      # 添加 [CLS] 和 [SEP]
            max_length=max_length,         # 最大长度
            padding='max_length',          # 填充到最大长度
            truncation=True,               # 截断超长文本
            return_tensors='pt'            # 返回 PyTorch tensor
        )
        
        encodings.append(encoding)
        labels.append(label)
    
    return encodings, labels

print("正在处理训练集...")
train_encodings, train_labels = preprocess_data(train_data, tokenizer)

print(f"✓ 训练集处理完成")
print(f"  输入形状:{train_encodings[0]['input_ids'].shape}")
print(f"  标签数量:{len(train_labels)}")

# ============================================================================
# 第四部分:创建 Dataset 类
# ============================================================================
print("\n" + "=" * 60)
print("【4. 创建 Dataset】")
print("=" * 60)

class StockCommentDataset(torch.utils.data.Dataset):
    """股票评论数据集"""
    
    def __init__(self, encodings, labels):
        self.encodings = encodings
        self.labels = labels
    
    def __getitem__(self, idx):
        item = {key: val.squeeze(0) for key, val in self.encodings[idx].items()}
        item['labels'] = torch.tensor(self.labels[idx])
        return item
    
    def __len__(self):
        return len(self.labels)

train_dataset = StockCommentDataset(train_encodings, train_labels)

print(f"✓ Dataset 创建完成")
print(f"  样本数量:{len(train_dataset)}")

# ============================================================================
# 第五部分:定义评估函数
# ============================================================================
print("\n" + "=" * 60)
print("【5. 定义评估指标】")
print("=" * 60)

def compute_metrics(pred):
    """计算准确率等评估指标"""
    labels = pred.label_ids
    preds = pred.predictions.argmax(-1)
    
    acc = accuracy_score(labels, preds)
    
    return {
        'accuracy': acc,
    }

print("✓ 评估指标定义完成")
print("  - 准确率 (accuracy)")

# ============================================================================
# 第六部分:训练模型
# ============================================================================
print("\n" + "=" * 60)
print("【6. 训练模型】")
print("=" * 60)

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=3,
    per_device_train_batch_size=8,
    learning_rate=2e-5,
    warmup_steps=10,
    logging_dir='./logs',
    logging_steps=10,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
)

print("训练参数:")
print(f"  - 训练轮数:{training_args.num_train_epochs}")
print(f"  - 批次大小:{training_args.per_device_train_batch_size}")
print(f"  - 学习率:{training_args.learning_rate}")

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    compute_metrics=compute_metrics,
)

print("\n开始训练...")
print("提示:由于数据量小,训练很快完成")

trainer.train()

print("\n✓ 模型训练完成")

# ============================================================================
# 第七部分:模型评估
# ============================================================================
print("\n" + "=" * 60)
print("【7. 模型评估】")
print("=" * 60)

# 处理测试集
test_encodings, test_labels = preprocess_data(test_data, tokenizer)
test_dataset = StockCommentDataset(test_encodings, test_labels)

print("在测试集上评估...")

eval_results = trainer.evaluate(test_dataset)

print(f"\n测试结果:")
print(f"  - 准确率:{eval_results['eval_accuracy']:.2%}")

# ============================================================================
# 第八部分:实际预测
# ============================================================================
print("\n" + "=" * 60)
print("【8. 实际预测】")
print("=" * 60)

def predict_sentiment(comment_text, model, tokenizer):
    """预测单条评论的情感"""
    
    # 编码
    inputs = tokenizer.encode_plus(
        comment_text,
        add_special_tokens=True,
        max_length=128,
        padding='max_length',
        truncation=True,
        return_tensors='pt'
    )
    
    # 预测
    with torch.no_grad():
        outputs = model(**inputs)
        predictions = torch.softmax(outputs.logits, dim=-1)
        probs = predictions[0].numpy()
        label = torch.argmax(predictions, dim=-1).item()
    
    # 标签映射
    label_map = {0: "负面", 1: "正面", 2: "中性"}
    
    return label_map[label], probs[label]

# 测试一些新的评论
test_comments = [
    "这家公司的前景非常好,值得投资",
    "完了完了,股价要崩盘了",
    "今天股价波动不大,正常震荡",
    "业绩大幅增长,超出市场预期",
    "产品销量下滑,面临激烈竞争",
]

print("\n实时预测结果:\n")

for comment in test_comments:
    label, confidence = predict_sentiment(comment, model, tokenizer)
    
    # 显示表情符号
    if label == "正面":
        emoji = "🟢"
    elif label == "负面":
        emoji = "🔴"
    else:
        emoji = "⚪"
    
    print(f"{emoji} {comment}")
    print(f"   情感:{label} (置信度:{confidence:.2%})")
    print()

print("\n🎊 情感分析系统完成!")
print("=" * 60)

# ============================================================================
# 第九部分:可视化结果
# ============================================================================
print("\n" + "=" * 60)
print("【9. 可视化分析】")
print("=" * 60)

# 绘制混淆矩阵(如果有测试集预测结果)
try:
    predictions_output = trainer.predict(test_dataset)
    preds = predictions_output.predictions.argmax(-1)
    
    cm = confusion_matrix(test_labels, preds)
    
    plt.figure(figsize=(8, 6))
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
    plt.title('混淆矩阵')
    plt.colorbar()
    
    classes = ['负面', '正面', '中性']
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes)
    plt.yticks(tick_marks, classes)
    
    # 在每个格子中显示数值
    thresh = cm.max() / 2.
    for i in range(cm.shape[0]):
        for j in range(cm.shape[1]):
            plt.text(j, i, format(cm[i, j], 'd'),
                    horizontalalignment="center",
                    color="white" if cm[i, j] > thresh else "black")
    
    plt.ylabel('真实标签')
    plt.xlabel('预测标签')
    plt.tight_layout()
    plt.show()
    
except:
    print("混淆矩阵生成失败(数据量太小),跳过此步骤")

print("\n💡 实际应用建议:")
print("""
使用场景推荐:

1. 股票论坛舆情监控:
   ✓ 实时抓取雪球、东方财富评论
   ✓ 自动判断情感倾向
   ✓ 发现情绪异常波动
   ✓ 辅助投资决策

2. 新闻媒体分析:
   ✓ 监控财经新闻情感
   ✓ 统计利好/利空消息比例
   ✓ 跟踪舆情变化趋势
   ✓ 提前发现风险

3. 智能客服质检:
   ✓ 分析用户反馈情感
   ✓ 发现不满意客户
   ✓ 及时介入处理
   ✓ 提升服务质量

4. 产品口碑监控:
   ✓ 收集电商评论
   ✓ 分析用户满意度
   ✓ 发现产品问题
   ✓ 改进产品质量

技术要点:

✓ 数据质量很重要
  - 标注准确的训练数据
  - 多样化的样本
  - 平衡的类别分布

✓ 模型选择
  - BERT: 效果好,速度慢
  - DistilBERT: 速度快,效果略差
  - RoBERTa: 效果更好,需要更多数据

✓ 阈值设置
  - 高置信度才采纳
  - 低置信度的转人工
  - 平衡准确率和覆盖率

常见问题:

✗ 讽刺和反语识别不了
  → "真是太好了(其实是反话)"
  → 需要更复杂的模型
  
✗ 领域差异
  → 医疗、法律等专业领域
  → 需要领域特定的训练数据
  
✗ 多义词问题
  → "这个股票很火"(好)
  → "着火了"(坏)
  → 需要上下文理解
""")

print("\n🎉 情感分析实战完成!")
print("=" * 60)

按 Shift + Enter 运行!


🎯 费曼输出 #2:深入理解技术

任务 1:解释技术细节

思考题:

  1. BERT 为什么比关键词匹配好?
  2. 训练数据的质量对模型有什么影响?
  3. 如何处理中性评论的边界情况?
  4. 如果要扩展到 5 星评分,需要怎么改?

任务 2:设计监控系统

场景:你要帮股民建立一个舆情监控仪表板

要求:

  1. 设计数据源(从哪里抓取评论)
  2. 设计更新频率(多久分析一次)
  3. 设计告警机制(什么情况触发警报)
  4. 设计可视化界面(如何展示结果)

⏰ 时间:30 分钟


💡 卡壳检查点

  • 我解释不清 BERT 的优势在哪里
  • 我说不明白训练数据的重要性
  • 我不能设计实际的监控系统

提示:

  • BERT = 理解上下文,不是死记关键词
  • 数据 = 模型的基础,垃圾进垃圾出
  • 中性 = 介于正负之间,最难判断

💻 第 4 步:实战项目(80 分钟)

项目:雪球论坛情感监控系统

"""
项目背景:
雪球是中国最大的投资者社区
每天有数百万条评论
用 AI 实时监控情绪变化
发现投资机会和风险

功能:
1. 实时抓取热门股票评论
2. 自动分析情感倾向
3. 统计多空比例
4. 发现情绪拐点
"""

import time
from datetime import datetime

print("=" * 60)
print("📈 雪球论坛情感监控系统")
print("=" * 60)

# 模拟实时数据流
mock_comments_stream = [
    ("贵州茅台", "茅台的业绩太稳了,长期持有没问题", 1),
    ("贵州茅台", "估值太高了,随时可能回调", 0),
    ("贵州茅台", "今天股价波动不大,正常", 2),
    ("宁德时代", "新能源前景广阔,坚定看好", 1),
    ("宁德时代", "竞争太激烈,利润会被压缩", 0),
    ("比亚迪", "销量创新高,股价要起飞", 1),
    ("比亚迪", "补贴退坡,影响很大", 0),
    ("腾讯控股", "游戏业务回暖,推荐买入", 1),
    ("腾讯控股", "政策监管风险大,谨慎", 0),
    ("阿里巴巴", "组织架构调整,观望", 2),
]

# 情感统计
sentiment_stats = {
    'positive': 0,
    'negative': 0,
    'neutral': 0,
}

print("\n开始实时监控...\n")

for stock, comment, sentiment in mock_comments_stream:
    # 模拟处理延迟
    time.sleep(0.5)
    
    # 更新统计
    if sentiment == 1:
        sentiment_stats['positive'] += 1
        emoji = "🟢"
        label = "正面"
    elif sentiment == 0:
        sentiment_stats['negative'] += 1
        emoji = "🔴"
        label = "负面"
    else:
        sentiment_stats['neutral'] += 1
        emoji = "⚪"
        label = "中性"
    
    # 计算比例
    total = sum(sentiment_stats.values())
    positive_ratio = sentiment_stats['positive'] / total * 100
    negative_ratio = sentiment_stats['negative'] / total * 100
    
    # 显示结果
    timestamp = datetime.now().strftime("%H:%M:%S")
    print(f"[{timestamp}] {emoji} {stock}: {comment}")
    print(f"        情感:{label}")
    print(f"        多空比:🟢{positive_ratio:.1f}% : 🔴{negative_ratio:.1f}%")
    print()
    
    # 检测情绪异常
    if total >= 5:
        if positive_ratio > 80:
            print("  ⚠️  警报:过度乐观,注意风险!")
        elif negative_ratio > 80:
            print("  ⚠️  警报:过度悲观,可能是机会!")
        print()

# 最终统计
print("\n" + "=" * 60)
print("最终统计结果:")
print("=" * 60)
print(f"总评论数:{total} 条")
print(f"正面:{sentiment_stats['positive']} 条 ({positive_ratio:.1f}%)")
print(f"负面:{sentiment_stats['negative']} 条 ({negative_ratio:.1f}%)")
print(f"中性:{sentiment_stats['neutral']} 条 ({sentiment_stats['neutral']/total*100:.1f}%)")

if positive_ratio > negative_ratio:
    print(f"\n整体情绪:🟢 偏向乐观")
elif negative_ratio > positive_ratio:
    print(f"\n整体情绪:🔴 偏向悲观")
else:
    print(f"\n整体情绪:⚪ 中性")

print("\n💡 使用建议:")
print("  1. 结合技术指标一起看")
print("  2. 不要盲目跟风")
print("  3. 警惕过度一致的情绪")
print("  4. 逆向思维:别人贪婪我恐惧")

print("\n🎊 监控系统演示完成!")
print("=" * 60)

🎉 今日费曼总结(30 分钟)⭐

完整的费曼学习流程

第 1 步:回顾今天的内容(5 分钟)

  • 情感分析的基本概念
  • BERT 情感分类原理
  • 股票评论监控系统

第 2 步:合上教程,尝试完整教授(15 分钟)⭐

任务:假装你在给一个完全不懂的人上第二十四堂课

要覆盖:

  1. 情感分析是怎么工作的(用至少 2 个比喻)
  2. BERT 相比传统方法的优势
  3. 演示舆情监控系统
  4. 讲解实际应用价值

方式:写一篇 800 字左右的文章,或录一段 10-15 分钟的视频

第 3 步:标记卡壳点(5 分钟)

我今天卡壳的地方: □ _________________________________ □ _________________________________

第 4 步:针对性复习(5 分钟)

回到教程中卡壳的地方,重新学习,然后再次尝试解释!


📝 费曼学习笔记模板

╔═══════════════════════════════════════════════════╗
║         Day 24 费曼学习笔记                       ║
╠═══════════════════════════════════════════════════╣
║ 日期:__________                                  ║
║ 学习时长:__________                              ║
╠═══════════════════════════════════════════════════╣
║                                                   ║
║ 1. 我向小白解释了:                               ║
║ _______________________________________________  ║
║                                                   ║
║ 2. 我卡壳的地方:                                 ║
║ □ _____________________________________________  ║
║                                                   ║
║ 3. 我的通俗比喻:                                 ║
║ • 情感分析就像 ______                             ║
║ • BERT 理解上下文就像 ______                      ║
║ • 舆情监控就像 ______                             ║
║                                                   ║
╚═══════════════════════════════════════════════════╝

📊 今日总结

✅ 你今天学到了:

  1. 情感分析基础

    • 二元/多元分类
    • 细粒度情感
    • 复杂情感处理
  2. BERT 情感分类

    • 上下文理解
    • 预训练 + 微调
    • 评估指标
  3. 实战应用

    • 股票评论分析
    • 舆情监控
    • 情绪预警
  4. 费曼输出能力 ⭐

    • 能用比喻解释情感分析
    • 能向小白说明 BERT 优势
    • 能完整讲解监控系统

🎁 明日预告

明天你将学习:强化学习入门

内容:

  • Q-learning 基础
  • DQN 算法
  • 实战:股票交易策略
  • 应用:AI 自动炒股

准备好让 AI 学会自主决策了吗?继续前进!🚀


🔗 相关链接

🌐 项目资源


本教程属于 AI 入门 30 天挑战 系列


🎉 恭喜你完成今天的学习!


🔗 资源汇总

💬 互动时间

思考题:今天的知识点中,哪个让你印象最深刻?为什么?

欢迎在评论区分享你的想法或疑问!👇

❤️ 如果有帮助

  • 👍 点赞:让更多人看到这篇教程
  • ⭐ Star GitHub:获取完整代码和项目
  • ➕ 关注专栏:不错过后续更新
  • 🔄 分享给朋友:一起学习进步

明天见!继续 Day 25 的学习~ 🚀


💡 学习建议

如果本篇教程对你有帮助,欢迎:

  1. Star GitHub 项目https://github.com/Lee985-cmd/AI-30Days-Challenge
  2. 留言交流你的学习困惑

一起学习,一起进步! 🤝

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐