You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TensorFlow Keras报错AttributeError: 'tuple'无lower属性的解决方法

TensorFlow Keras聊天机器人AttributeError修复方案

错误原因

tokenizer.fit_on_texts()方法要求输入字符串列表,但你传入的conversations是元组列表(每个元素是(输入文本, 回复文本)),方法内部尝试对每个元素调用lower()时,元组没有该属性,因此抛出AttributeError: 'tuple' object has no attribute 'lower'。

修复方案

方案一:合并所有对话文本训练Tokenizer

将对话对中的输入和回复文本全部提取出来,组成纯字符串列表供Tokenizer训练,同时拆分输入与回复的序列处理逻辑:

  1. 修改Tokenizer训练部分:
conversations = [
    ("Hello", "Hi there!"),
    ("How are you?", "I'm doing well, thanks."),
    ("What's your name?", "I'm a chatbot."),
]

tokenizer = Tokenizer()
# 提取所有对话中的文本(输入+回复)
all_texts = [text for pair in conversations for text in pair]
tokenizer.fit_on_texts(all_texts)

vocab_size = len(tokenizer.word_index) + 1
  1. 拆分输入与回复的序列转换和填充:
# 拆分输入文本和回复文本
inputs, responses = zip(*conversations)
# 分别转换为序列
X_sequences = tokenizer.texts_to_sequences(inputs)
y_sequences = tokenizer.texts_to_sequences(responses)
# 计算全局最大序列长度
max_sequence_len = max(
    max(len(seq) for seq in X_sequences),
    max(len(seq) for seq in y_sequences)
)
# 填充输入和回复序列
X = pad_sequences(X_sequences, maxlen=max_sequence_len, padding='post')
y = pad_sequences(y_sequences, maxlen=max_sequence_len, padding='post')
  1. 调整模型训练的损失函数适配(可选但建议):
    原代码使用sparse_categorical_crossentropy,需确保目标数据y为整数类型,若训练时出现维度不匹配,可将y转换为one-hot编码并改用categorical_crossentropy:
# 转换y为one-hot编码
y = tf.keras.utils.to_categorical(y, num_classes=vocab_size)
# 重新编译模型
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

方案二:分别为输入和回复创建Tokenizer(适用于复杂对话场景)

如果需要区分输入和回复的词汇表,可以创建两个独立的Tokenizer:

# 拆分输入和回复
inputs, responses = zip(*conversations)

# 输入文本Tokenizer
input_tokenizer = Tokenizer()
input_tokenizer.fit_on_texts(inputs)
input_vocab_size = len(input_tokenizer.word_index) + 1

# 回复文本Tokenizer
response_tokenizer = Tokenizer()
response_tokenizer.fit_on_texts(responses)
response_vocab_size = len(response_tokenizer.word_index) + 1

# 处理序列
X_sequences = input_tokenizer.texts_to_sequences(inputs)
y_sequences = response_tokenizer.texts_to_sequences(responses)

max_input_len = max(len(seq) for seq in X_sequences)
max_response_len = max(len(seq) for seq in y_sequences)
# 统一使用最大长度或分别设置,根据模型需求调整
max_sequence_len = max(max_input_len, max_response_len)

X = pad_sequences(X_sequences, maxlen=max_sequence_len, padding='post')
y = pad_sequences(y_sequences, maxlen=max_sequence_len, padding='post')

# 模型部分需调整Embedding层的vocab_size为input_vocab_size,输出层为response_vocab_size
model = Sequential([
    Embedding(input_vocab_size, 64, input_length=max_sequence_len, mask_zero=True),
    LSTM(100, return_sequences=True),
    Dense(response_vocab_size, activation='softmax')
])

修改后的完整代码(方案一)

import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense

conversations = [
    ("Hello", "Hi there!"),
    ("How are you?", "I'm doing well, thanks."),
    ("What's your name?", "I'm a chatbot."),
]

tokenizer = Tokenizer()
all_texts = [text for pair in conversations for text in pair]
tokenizer.fit_on_texts(all_texts)

vocab_size = len(tokenizer.word_index) + 1

inputs, responses = zip(*conversations)
X_sequences = tokenizer.texts_to_sequences(inputs)
y_sequences = tokenizer.texts_to_sequences(responses)

max_sequence_len = max(
    max(len(seq) for seq in X_sequences),
    max(len(seq) for seq in y_sequences)
)

X = pad_sequences(X_sequences, maxlen=max_sequence_len, padding='post')
y = pad_sequences(y_sequences, maxlen=max_sequence_len, padding='post')

# 转换为one-hot编码适配categorical_crossentropy
y = tf.keras.utils.to_categorical(y, num_classes=vocab_size)

model = Sequential([
    Embedding(vocab_size, 64, input_length=max_sequence_len, mask_zero=True),
    LSTM(100, return_sequences=True),
    Dense(vocab_size, activation='softmax')
])

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

model.fit(X, y, epochs=50, verbose=1)

def generate_response(input_text):
    input_seq = tokenizer.texts_to_sequences([input_text])
    padded_input = pad_sequences(input_seq, maxlen=max_sequence_len, padding='post')
    predicted_output = model.predict(padded_input)
    # 对每个时间步取概率最大的词索引
    predicted_word_index = tf.argmax(predicted_output, axis=-1).numpy()
    response = tokenizer.sequences_to_texts(predicted_word_index)
    # 过滤掉填充的空字符串
    return ' '.join([word for word in response[0].split() if word != ''])

while True:
    user_input = input(">>> ")
    response = generate_response(user_input)
    print(f"Chatbot: {response}")

内容的提问来源于stack exchange,提问作者Milos Cecaric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:48:18