TensorFlow Keras报错AttributeError: 'tuple'无lower属性的解决方法
TensorFlow Keras聊天机器人AttributeError修复方案
错误原因
tokenizer.fit_on_texts()方法要求输入字符串列表,但你传入的conversations是元组列表(每个元素是(输入文本, 回复文本)),方法内部尝试对每个元素调用lower()时,元组没有该属性,因此抛出AttributeError: 'tuple' object has no attribute 'lower'。
修复方案
方案一:合并所有对话文本训练Tokenizer
将对话对中的输入和回复文本全部提取出来,组成纯字符串列表供Tokenizer训练,同时拆分输入与回复的序列处理逻辑:
- 修改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
- 拆分输入与回复的序列转换和填充:
# 拆分输入文本和回复文本 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')
- 调整模型训练的损失函数适配(可选但建议):
原代码使用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
相关产品推荐
相关产品推荐

