能否在神经网络中使用不同长度的输入文本数据?
处理机器学习中变长文本的编码问题
当然可以处理不同长度的文本向量啦!你现在的代码之所以只能适配固定长度文本,核心原因是用了固定输入维度的全连接层(input_dim=11),而且编码逻辑直接把字符转成索引后就喂入模型,没有处理变长序列的机制。下面给你几个实用的解决方案,从简单到进阶都有:
方案1:填充/截断(Padding/Truncation)
这是最直接的适配方法,把所有文本统一到一个固定长度:
- 先统计所有文本的长度,选一个合适的最大长度(比如最长文本的长度,或者取数据中95%文本都不超过的长度)
- 短文本在末尾填充特殊标记(比如0,要确保这个标记不在你的字符字典里)
- 长文本直接截断到最大长度
修改后的代码示例:
import pandas as pd import csv import numpy as np from keras.models import Sequential from keras.layers import Dense # 读取数据 train_set = pd.read_csv("train3.tsv", sep="\t", header=None, names=['score', 'text'], skip_blank_lines=False, quoting=csv.QUOTE_NONE, error_bad_lines=False) test_set = pd.read_csv("test.tsv", sep="\t", header=None, names=['text'], skip_blank_lines=False, quoting=csv.QUOTE_NONE, error_bad_lines=False) # 构建字符字典(仅用训练集字符,避免测试集引入新索引) dictionary = {} i = 1 # 留0作为填充标记 for text in train_set.text.str.lower(): for char in list(text): if char not in dictionary: dictionary[char] = i i += 1 pad_token = 0 # 确定最大长度 max_len = max([len(text) for text in train_set.text] + [len(text) for text in test_set.text]) # 编码并填充/截断训练集 train_encoded = [] for text in train_set.text.str.lower(): encoded = [dictionary[char] for char in list(text)] # 填充或截断到固定长度 if len(encoded) < max_len: encoded += [pad_token] * (max_len - len(encoded)) else: encoded = encoded[:max_len] train_encoded.append(encoded) # 处理测试集(未知字符用填充标记代替) test_encoded = [] for text in test_set.text.str.lower(): encoded = [dictionary.get(char, pad_token) for char in list(text)] if len(encoded) < max_len: encoded += [pad_token] * (max_len - len(encoded)) else: encoded = encoded[:max_len] test_encoded.append(encoded) # 准备模型输入数据 score = np.array(train_set.score.values) text = np.array(train_encoded) test = np.array(test_encoded) # 创建模型(input_dim改为max_len) model = Sequential() model.add(Dense(12, input_dim=max_len, init='uniform')) model.add(Dense(11, init='uniform')) model.add(Dense(1, init='uniform', activation='sigmoid')) # 二分类必须加sigmoid激活 # 编译并训练 model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) model.fit(text, score, epochs=150, batch_size=10, verbose=2) # 预测 predictions = model.predict(test) print(predictions)
方案2:使用循环神经网络(LSTM/GRU)
循环神经网络天生适合处理变长序列,能捕捉字符的顺序信息,效果通常比全连接层更好。搭配Embedding层可以把字符索引转成稠密向量:
import pandas as pd import csv import numpy as np from keras.models import Sequential from keras.layers import Embedding, LSTM, Dense from keras.preprocessing.sequence import pad_sequences # 读取数据、构建字典步骤和方案1一致,此处省略重复代码... # 编码保留原长度 train_encoded = [[dictionary[char] for char in list(text.lower())] for text in train_set.text] test_encoded = [[dictionary.get(char, pad_token) for char in list(text.lower())] for text in test_set.text] # 统一序列长度(LSTM仍需固定输入长度,但内部会处理变长逻辑) max_len = max([len(seq) for seq in train_encoded + test_encoded]) train_padded = pad_sequences(train_encoded, maxlen=max_len, padding='post', truncating='post') test_padded = pad_sequences(test_encoded, maxlen=max_len, padding='post', truncating='post') # 创建序列模型 model = Sequential() # Embedding层:输入维度是字典大小,输出维度是嵌入向量长度,输入长度是max_len model.add(Embedding(input_dim=len(dictionary)+1, output_dim=32, input_length=max_len)) model.add(LSTM(64)) # LSTM隐藏单元数 model.add(Dense(1, activation='sigmoid')) # 编译训练 model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) model.fit(train_padded, score, epochs=50, batch_size=10, verbose=2) predictions = model.predict(test_padded) print(predictions)
方案3:字符级Bag of Characters
如果不想用神经网络,也可以用传统特征工程方法,把每个文本转成字符频率向量,不管文本多长,输出维度都是字符字典的大小:
from sklearn.feature_extraction.text import CountVectorizer from sklearn.linear_model import LogisticRegression # 读取数据步骤省略... # 用CountVectorizer统计字符频率 vectorizer = CountVectorizer(analyzer='char') train_features = vectorizer.fit_transform(train_set.text) test_features = vectorizer.transform(test_set.text) # 训练逻辑回归模型 model = LogisticRegression() model.fit(train_features, train_set.score) predictions = model.predict_proba(test_features)[:, 1] print(predictions)
关键注意事项
- 绝对不要在测试集里给新字符分配新索引!这会导致训练和测试的字典不一致,模型完全无法正确预测。测试集的未知字符应该用统一标记代替。
- 二分类任务的输出层一定要加
activation='sigmoid',否则输出范围不符合损失函数的要求。 - 如果文本长度差异很大,优先选LSTM这类序列模型,它能更好地利用字符的顺序信息。
内容的提问来源于stack exchange,提问作者Wahtd
相关产品推荐
相关产品推荐

