基于词袋模型(Bag-of-Words)的简单神经网络的对话

1. 概述

本代码实现了一个基于词袋模型(Bag-of-Words)和简单神经网络的对话系统。该系统能够对用户输入的文本进行预处理、转换为词袋向量,通过神经网络进行训练和预测,并根据不同的输入情境生成相应的回复。

2. 依赖库

python

复制代码
import numpy as np
import random
  • numpy:用于数值计算,包括矩阵运算、数组操作等,是实现神经网络计算的基础库。
  • random:用于在生成回复时进行随机选择,增加回复的多样性。

3. 函数说明

3.1 preprocess 函数

python

复制代码
def preprocess(text):
    """文本预处理(支持中英文)"""
    # 去除标点符号
    text = ''.join([c for c in text if c not in '.,?!,。!?;:""''()《》'])
    # 中英文分开处理
    if any('\u4e00' <= c <= '\u9fff' for c in text):  # 包含中文
        return [word for word in text.lower().strip() if word.strip()]
    else:  # 英文处理
        return text.lower().split()
  • 功能:对输入的文本进行预处理,包括去除标点符号,并根据文本是否包含中文进行不同的分词处理。
  • 参数
    • text:待处理的文本字符串。
  • 返回值:处理后的单词列表。

3.2 build_vocab 函数

python

复制代码
def build_vocab(sentences):
    """构建词汇表"""
    vocab = set()
    for sentence in sentences:
        vocab.update(preprocess(sentence))
    return list(vocab)
  • 功能:根据输入的句子列表构建词汇表,将所有句子中出现的单词合并去重。
  • 参数
    • sentences:句子列表。
  • 返回值:包含所有不重复单词的词汇表列表。

3.3 sentence_to_bow 函数

python

复制代码
def sentence_to_bow(sentence, vocab):
    """将句子转换为词袋向量"""
    bow = np.zeros(len(vocab))
    words = preprocess(sentence)
    for word in words:
        if word in vocab:
            bow[vocab.index(word)] += 1
    return bow
  • 功能:将输入的句子转换为词袋向量,向量的长度等于词汇表的长度,每个位置的数值表示该位置对应的单词在句子中出现的次数。
  • 参数
    • sentence:待转换的句子。
    • vocab:词汇表。
  • 返回值 :表示句子的词袋向量(numpy 数组)。

4. 类说明

4.1 NeuralNetwork

python

复制代码
class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.b1 = np.zeros((1, hidden_size))
        self.W2 = np.random.randn(hidden_size, output_size) * 0.01
        self.b2 = np.zeros((1, output_size))
  • 功能:定义一个简单的两层神经网络,包含输入层、隐藏层和输出层,并初始化网络的权重和偏置。
  • 参数
    • input_size:输入层的神经元数量,即词袋向量的长度。
    • hidden_size:隐藏层的神经元数量。
    • output_size:输出层的神经元数量,通常等于词汇表的长度。
  • 属性
    • W1:输入层到隐藏层的权重矩阵。
    • b1:隐藏层的偏置向量。
    • W2:隐藏层到输出层的权重矩阵。
    • b2:输出层的偏置向量。
4.1.1 forward 方法

python

复制代码
    def forward(self, X):
        self.z1 = np.dot(X, self.W1) + self.b1
        self.a1 = np.tanh(self.z1)
        self.z2 = np.dot(self.a1, self.W2) + self.b2
        self.a2 = self.softmax(self.z2)
        return self.a2
  • 功能:实现神经网络的前向传播过程,计算输入数据经过网络各层后的输出。
  • 参数
    • X:输入数据,形状为 (样本数量, 输入层大小)numpy 数组。
  • 返回值 :输出层的激活值,形状为 (样本数量, 输出层大小)numpy 数组。
4.1.2 softmax 方法

python

复制代码
    def softmax(self, x):
        exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
        return exp_x / np.sum(exp_x, axis=1, keepdims=True)
  • 功能:实现 Softmax 激活函数,将输入的数值转换为概率分布。
  • 参数
    • x:输入数据,形状为 (样本数量, 输出层大小)numpy 数组。
  • 返回值 :经过 Softmax 激活后的概率分布,形状为 (样本数量, 输出层大小)numpy 数组。
4.1.3 train 方法

python

复制代码
    def train(self, X, y, epochs=1000, learning_rate=0.01):
        for epoch in range(epochs):
            output = self.forward(X)
            loss = -np.sum(y * np.log(output + 1e-10)) / len(X)
            
            dZ2 = output - y
            dW2 = np.dot(self.a1.T, dZ2)
            db2 = np.sum(dZ2, axis=0, keepdims=True)
            dZ1 = np.dot(dZ2, self.W2.T) * (1 - np.power(self.a1, 2))
            dW1 = np.dot(X.T, dZ1)
            db1 = np.sum(dZ1, axis=0)
            
            self.W2 -= learning_rate * dW2
            self.b2 -= learning_rate * db2
            self.W1 -= learning_rate * dW1
            self.b1 -= learning_rate * db1
            
            if epoch % 100 == 0:
                print(f"Epoch {epoch}, Loss: {loss:.4f}")
  • 功能:对神经网络进行训练,使用梯度下降法更新网络的权重和偏置,以最小化交叉熵损失。
  • 参数
    • X:训练数据的输入,形状为 (样本数量, 输入层大小)numpy 数组。
    • y:训练数据的目标输出,形状为 (样本数量, 输出层大小)numpy 数组。
    • epochs:训练的轮数,默认为 1000
    • learning_rate:学习率,控制每次参数更新的步长,默认为 0.01
  • 返回值:无

5. 对话数据

python

复制代码
# 增强版对话数据(更自然的表达)
conversations = [
    {"input": "hello", "output": "您好!很高兴为您服务,请问有什么可以帮您的?"},
    # 更多对话示例...
]

conversations 是一个列表,其中每个元素是一个字典,包含 "input"(用户输入的文本)和 "output"(对应的回复文本)键值对,用于训练和测试对话系统。

6. 模型训练与初始化

python

复制代码
# 初始化模型参数
inputs = [conv["input"] for conv in conversations]
outputs = [conv["output"] for conv in conversations]
vocab = build_vocab(inputs + outputs)
X = np.array([sentence_to_bow(conv["input"], vocab) for conv in conversations])
y = np.array([sentence_to_bow(conv["output"], vocab) for conv in conversations])

model = NeuralNetwork(len(vocab), 16, len(vocab))
model.train(X, y, epochs=2000, learning_rate=0.05)
  • 功能:从对话数据中提取输入和输出文本,构建词汇表,将输入和输出文本转换为词袋向量,创建神经网络模型并进行训练。
  • 步骤
    1. conversations 中分别提取用户输入和回复文本。
    2. 使用 build_vocab 构建词汇表。
    3. 使用 sentence_to_bow 将输入和输出文本转换为词袋向量。
    4. 创建 NeuralNetwork 模型实例。
    5. 使用转换后的词袋向量数据对模型进行训练。

7. predict_response 函数

python

复制代码
def predict_response(input_text, vocab, model):
    """生成更自然的回复"""
    bow = sentence_to_bow(input_text, vocab)
    prob = model.forward(np.array([bow]))
    
    # 使用温度采样并添加数值稳定性处理
    temperature = 0.7
    prob = np.clip(prob, 1e-10, 1.0)  # 防止log(0)
    scaled_probs = np.exp(np.log(prob) / temperature)
    scaled_probs /= scaled_probs.sum()
    
    # 选择前3候选词
    top3_idx = np.argsort(scaled_probs)[0][-3:]
    candidates = [vocab[i] for i in top3_idx]
    
    # 上下文感知回复(增强版)
    lower_input = input_text.lower()
    
    # 多种情况处理,如问候、道别、问题、感谢等...
    # 生成更自然的句子结构或默认回退响应...
  • 功能:根据用户输入的文本,生成自然的回复。先将输入文本转换为词袋向量,通过神经网络预测概率分布,然后根据不同的输入情境(如问候、道别、问题、感谢等)选择合适的回复策略。
  • 参数
    • input_text:用户输入的文本字符串。
    • vocab:词汇表。
    • model:训练好的神经网络模型。
  • 返回值:生成的回复文本字符串。

8. 对话系统启动

python

复制代码
# 启动对话系统
print("Chatbot: 您好!我是Claude,有什么可以帮您?(输入quit退出)")
while True:
    user_input = input("You: ")
    if user_input.lower() == "quit":
        print("Chatbot: 再见!祝您有美好的一天!")
        break
    response = predict_response(user_input, vocab, model)
    print(f"Chatbot: {response}")
  • 功能 :启动对话系统,持续接收用户输入,调用 predict_response 函数生成回复并输出,直到用户输入 "quit" 退出系统。

完整代码

python 复制代码
import numpy as np
import random

def preprocess(text):
    """文本预处理(支持中英文)"""
    # 去除标点符号
    text = ''.join([c for c in text if c not in '.,?!,。!?;:""''()《》'])
    # 中英文分开处理
    if any('\u4e00' <= c <= '\u9fff' for c in text):  # 包含中文
        return [word for word in text.lower().strip() if word.strip()]
    else:  # 英文处理
        return text.lower().split()

def build_vocab(sentences):
    """构建词汇表"""
    vocab = set()
    for sentence in sentences:
        vocab.update(preprocess(sentence))
    return list(vocab)

def sentence_to_bow(sentence, vocab):
    """将句子转换为词袋向量"""
    bow = np.zeros(len(vocab))
    words = preprocess(sentence)
    for word in words:
        if word in vocab:
            bow[vocab.index(word)] += 1
    return bow

class NeuralNetwork:
    def __init__(self, input_size, hidden_size, output_size):
        self.W1 = np.random.randn(input_size, hidden_size) * 0.01
        self.b1 = np.zeros((1, hidden_size))
        self.W2 = np.random.randn(hidden_size, output_size) * 0.01
        self.b2 = np.zeros((1, output_size))

    def forward(self, X):
        self.z1 = np.dot(X, self.W1) + self.b1
        self.a1 = np.tanh(self.z1)
        self.z2 = np.dot(self.a1, self.W2) + self.b2
        self.a2 = self.softmax(self.z2)
        return self.a2

    def softmax(self, x):
        exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
        return exp_x / np.sum(exp_x, axis=1, keepdims=True)

    def train(self, X, y, epochs=1000, learning_rate=0.01):
        for epoch in range(epochs):
            output = self.forward(X)
            loss = -np.sum(y * np.log(output + 1e-10)) / len(X)
            
            dZ2 = output - y
            dW2 = np.dot(self.a1.T, dZ2)
            db2 = np.sum(dZ2, axis=0, keepdims=True)
            dZ1 = np.dot(dZ2, self.W2.T) * (1 - np.power(self.a1, 2))
            dW1 = np.dot(X.T, dZ1)
            db1 = np.sum(dZ1, axis=0)
            
            self.W2 -= learning_rate * dW2
            self.b2 -= learning_rate * db2
            self.W1 -= learning_rate * dW1
            self.b1 -= learning_rate * db1
            
            if epoch % 100 == 0:
                print(f"Epoch {epoch}, Loss: {loss:.4f}")

# 增强版对话数据(更自然的表达)
conversations = [
    {"input": "hello", "output": "您好!很高兴为您服务,请问有什么可以帮您的?"},
    {"input": "hi", "output": "嗨!今天过得怎么样?有什么需要我帮忙的吗?"},
    {"input": "how are you", "output": "我很好,谢谢关心!您今天有什么计划吗?"},
    {"input": "what's your name", "output": "我是您的智能助手Claude,随时为您效劳。"},
    {"input": "bye", "output": "再见啦!期待下次再聊,祝您一切顺利!"},
    {"input": "see you", "output": "好的,有需要随时找我哦!"},
    {"input": "thanks", "output": "您太客气了!能帮到您是我的荣幸。"},
    {"input": "what's this", "output": "您可以多描述一些细节吗?这样我可以更好地理解您的问题。"},
    {"input": "help", "output": "请告诉我您需要哪方面的帮助,我会尽力为您解答。"},
    {"input": "not sure", "output": "没关系,我们可以慢慢聊。您最近对什么领域比较感兴趣呢?"},
    {"input": "what do you think", "output": "这是一个很有意思的观点,您能详细说说您的想法吗?"},
    {"input": "interesting", "output": "看来您发现了一些有趣的事情,愿意和我分享更多细节吗?"},
    {"input": "how to learn", "output": "学习新知识很棒!您想重点了解哪个领域的内容呢?"},
    {"input": "what's the weather like today", "output": "很抱歉,我无法直接获取实时天气呢。您可以查看一下天气预报软件哦。"},
    {"input": "今天天气怎么样", "output": "我没办法直接知道当前的天气情况呢,您可以通过手机上的天气应用查询一下呀。"},
    {"input": "what time is it", "output": "我没办法直接查看时间呢,您可以看看身边的时钟或者手机哦。"},
    {"input": "现在几点了", "output": "不好意思,我不能直接告诉您时间呢,您可以看看手表或者手机上的时间。"},
    {"input": "do you like music", "output": "我没有真正的喜好呢,但音乐是一种很棒的艺术形式!您最喜欢什么类型的音乐呢?"},
    {"input": "你喜欢看电影吗", "output": "我没有情感去喜欢或不喜欢呢,不过电影是很有趣的文化载体!您最近看了什么好看的电影呀?"},
    {"input": "what's your hobby", "output": "我没有自己的爱好哦,但我可以陪您聊各种有趣的话题!您平时喜欢做些什么呢?"},
    {"input": "你平时喜欢做什么", "output": "我没有实际的活动爱好呢,不过我很乐意了解您的兴趣爱好!能和我分享一下吗?"},
]

# 初始化模型参数
inputs = [conv["input"] for conv in conversations]
outputs = [conv["output"] for conv in conversations]
vocab = build_vocab(inputs + outputs)
X = np.array([sentence_to_bow(conv["input"], vocab) for conv in conversations])
y = np.array([sentence_to_bow(conv["output"], vocab) for conv in conversations])

model = NeuralNetwork(len(vocab), 16, len(vocab))
model.train(X, y, epochs=2000, learning_rate=0.05)

def predict_response(input_text, vocab, model):
    """生成更自然的回复"""
    bow = sentence_to_bow(input_text, vocab)
    prob = model.forward(np.array([bow]))
    
    # 使用温度采样并添加数值稳定性处理
    temperature = 0.7
    prob = np.clip(prob, 1e-10, 1.0)  # 防止log(0)
    scaled_probs = np.exp(np.log(prob) / temperature)
    scaled_probs /= scaled_probs.sum()
    
    # 选择前3候选词
    top3_idx = np.argsort(scaled_probs)[0][-3:]
    candidates = [vocab[i] for i in top3_idx]
    
    # 上下文感知回复(增强版)
    lower_input = input_text.lower()
    
    # 问候处理
    if any(w in lower_input for w in ["你好", "嗨", "hello", "hi"]):
        return random.choice([
            "您好!有什么我可以帮忙的吗?",
            "嗨!今天想聊点什么?",
            "很高兴见到您!有什么疑问吗?"
        ])
        
    # 道别处理
    if any(w in lower_input for w in ["再见", "bye", "goodbye"]):
        return random.choice([
            "期待下次再聊!祝您有个美好的一天!",
            "再见!有任何问题随时找我哦~",
            "先聊到这啦,保持联系!"
        ])
    
    # 问题处理
    if any(w in lower_input for w in ["怎么", "如何", "怎样", "why", "how"]):
        return random.choice([
            "这个问题可以从以下几个方面来考虑:首先...",
            "关于这一点,我们可以先了解一下基本概念...",
            "您提的这个问题很有意思,我们需要结合具体案例来分析..."
        ])
    
    # 感谢处理
    if any(w in lower_input for w in ["谢谢", "感谢", "thanks"]):
        return random.choice([
            "您太客气了!这是应该的~",
            "能帮到您是我的荣幸!",
            "随时为您效劳,有其他问题尽管问我!"
        ])
    
    # 生成更自然的句子结构(增强版)
    if candidates:
        subject = candidates[-1]
        # 根据主题动态生成回复
        templates = [
            f"嗯,关于{subject}啊,其实我们可以这样来看...",
            f"{subject}这个话题挺有意思的,您是不是想了解{random.choice(['它的实际应用', '背后的原理', '最新发展'])}呢?",
            f"说到{subject},我想到最近有个案例挺典型的------比如说...",
            f"{subject}这个领域确实有些复杂,咱们先从最基础的{random.choice(['概念', '分类', '发展历程'])}说起吧?",
            f"您提到的{subject}让我联想到{random.choice(['另一个相关领域', '某位专家的观点', '近期的一个新闻'])},您觉得这之间有什么联系吗?",
            f"关于{subject},不同人有不同看法。有人觉得...,但也有人认为...,您更倾向哪种观点呢?",
            f"要深入理解{subject},可能需要考虑这几个方面:{random.choice(['技术实现', '社会影响', '经济效益'])}...",
            f"其实{subject}这个问题,我们可以用{random.choice(['类比的方法', '实际案例', '数据分析'])}来帮助理解..."
        ]
        return random.choice(templates)
    
    # 默认回退响应(带引导性提问)
    follow_up_questions = [
        ("这个方向确实值得探讨,", "能多说说您具体想了解的内容吗?"),
        ("很有意思的切入点,", "您比较关注实践应用还是理论层面呢?"),
        ("这个话题最近挺热门的,", "您是从什么渠道了解到相关信息的呀?"),
        ("您的视角很独特,", "能分享一下您目前的理解吗?"),
        ("这个问题确实值得深入研究,", "您需要我提供哪些方面的支持呢?")
    ]
    lead_in, question = random.choice(follow_up_questions)
    return f"{lead_in}{question}"

# 启动对话系统
print("Chatbot: 您好!我是Claude,有什么可以帮您?(输入quit退出)")
while True:
    user_input = input("You: ")
    if user_input.lower() == "quit":
        print("Chatbot: 再见!祝您有美好的一天!")
        break
    response = predict_response(user_input, vocab, model)
    print(f"Chatbot: {response}")
相关推荐
闪电麦坤9523 分钟前
C#:第一性原理拆解字段(fields)
开发语言·c#
csdn_aspnet24 分钟前
C# 简单数字时钟
c#
黄雪超43 分钟前
Java多线程与高并发专题——Condition 和 wait/notify的关系
java·开发语言·并发编程
三生暮雨渡瀟瀟1 小时前
Python控制结构详解
开发语言·python
罗婕斯特1 小时前
Scala中while和for循环
java·开发语言·前端
风雨中的蜜蜂1 小时前
HS6621CM-C是一款集成32 bit CPU、Flash和BLE/2.4G 的多模无线SoC芯片
c语言·开发语言
郭源潮11 小时前
《C++11:通过thread类编写C++多线程程序》
开发语言·c++·c++11
老马啸西风2 小时前
MOSN(Modular Open Smart Network)-08-MOSN 扩展机制解析
开发语言·网络·云原生·中间件·golang
老马啸西风2 小时前
MOSN(Modular Open Smart Network)-06-MOSN 多协议机制解析
开发语言·网络·云原生·中间件·golang
WineMonk2 小时前
C#使用用户名密码连接共享文件夹
windows·c#