为什么文本分类的char级cnn的acc保持不变

时间:2017-11-01 08:35:46

标签: nlp keras conv-neural-network

我误用了softmax的二元交叉熵,改为分类交叉熵。并在我自己的回答中对以下问题的详细信息进行了一些审核

我正在尝试使用开源数据:sogou_news_csv(使用 jieba 转换为拼音,用于{Zhang}和Yann LeCun之后的https://arxiv.org/abs/1502.01710“文字理解”之后的文本分类。主要遵循使用字符级CNN的想法,但文中提出的结构)。

我根据字母表集合使用单热编码进行预处理,并使用0填充所有不在字母表集合中的编码。 结果,我获得了形状为(450000,1000,70),(data_size,sequence_length,alphabet_size)的训练数据。

然后我将数据提供到http://www.wildml.com/2015/12/implementing-a-cnn-for-text-classification-in-tensorflow/之后的cnn结构中。

问题 在培训期间,损失和仅仅是更改,我再次尝试对数据进行预处理,tried different learning rate settings,但没有帮助,那么出了什么问题?

以下是单热编码:

import numpy as np

all_letters = "abcdefghijklmnopqrstuvwxyz0123456789-,;.!?:'\"/\\|_@#$%^&*~`+-=<>()[]{}\n"
n_letters = len(all_letters)

def letterToIndex(letter):
    """
    'c' -> 2
    """
    return all_letters.find(letter)


def sets2tensors(clean_train, n_letters=n_letters, MAX_SEQUENCE_LENGTH=1000):
    """
    From lists of cleaned passages to np.array with shape(len(train), 
        max_sequence_length, len(dict))
    Arg: 
        obviously
    """
    m = len(clean_train)
    x_data = np.zeros((m, MAX_SEQUENCE_LENGTH, n_letters))
    for ix in range(m):
        for no, letter in enumerate(clean_train[ix]):
            if no >= 1000:
                break
            letter_index = letterToIndex(letter)
            if letter != -1:
                x_data[ix][no][letter_index]  = 1
            else:
                continue            
    return x_data

这是模特:

num_classes = 5
from keras.models import Sequential
from keras.layers import Activation, GlobalMaxPool1D, Merge, concatenate, Conv1D, Dense, Dropout
from keras.callbacks import EarlyStopping
from keras.optimizers import SGD
submodels = []
for kw in (3, 4, 5):    # kernel sizes
    submodel = Sequential()
    submodel.add(Conv1D(32,
                        kw,
                        padding='valid',
                        activation='relu',
                        strides=1, input_shape=(1000, n_letters)))
    submodel.add(GlobalMaxPool1D())
    submodels.append(submodel)
big_model = Sequential()
big_model.add(Merge(submodels, mode="concat"))
big_model.add(Dense(64))
big_model.add(Dropout(0.5))
big_model.add(Activation('relu'))
big_model.add(Dense(num_classes))
big_model.add(Activation('softmax'))
print('Compiling model')
opt = SGD(lr=1e-6)  # tried different learning rate from 1e-6 to 1e-1
# changed from binary crossentropy to categorical_crossentropy
big_model.compile(loss='categorical_crossentropy', 
                  optimizer=opt,
                  metrics=['accuracy'])

一些结果

Train on 5000 samples, validate on 5000 samples
Epoch 1/5
5000/5000 [==============================] - 54s - loss: 0.5198 - acc: 0.7960 - val_loss: 0.5001 - val_acc: 0.8000
Epoch 2/5
5000/5000 [==============================] - 56s - loss: 0.5172 - acc: 0.7959 - val_loss: 0.5000 - val_acc: 0.8000
Epoch 3/5
5000/5000 [==============================] - 56s - loss: 0.5198 - acc: 0.7965 - val_loss: 0.5000 - val_acc: 0.8000
Epoch 4/5
5000/5000 [==============================] - 57s - loss: 0.5222 - acc: 0.7950 - val_loss: 0.4999 - val_acc: 0.8000
Epoch 5/5
5000/5000 [==============================] - 59s - loss: 0.5179 - acc: 0.7960 - val_loss: 0.4999 - val_acc: 0.8000

2 个答案:

答案 0 :(得分:1)

我发现问题是我偶然使用了softmax的二进制交叉熵(我用于另一个数据集),这应该是分类交叉熵。最初,我认为这是一个愚蠢的错误,因为我没有仔细检查代码和逻辑。

但后来我发现我并不真正理解这里发生了什么,我的意思是,我知道二元交叉熵和分类交叉熵之间的区别,但我不太了解细节为什么softmax和分类交叉熵不能链接在一起。

幸运的是,我在这里找到了一个非常好的解释(没想到有人真的会问或回答这个问题)

https://www.reddit.com/r/MachineLearning/comments/39bo7k/can_softmax_be_used_with_cross_entropy/#cs2b4jx

基本上它说的是在二进制交叉熵的情况下,损失函数将单个位的两个不同值视为两个不同的类:如A为1,B为0,尽管存在分类交叉熵情况,但损失函数采用类似[0,0,0,1,0]的向量作为标签,其中位的值代表相应训练示例的置信度或概率那个特殊的班级。

通过上面的描述,当我们将二进制交叉熵应用于softmax时,我们误用了一位在该设置中的含义的定义,因此没有意义。

答案 1 :(得分:0)

您已将SGD优化器设置为0.000001(opt = SGD(lr = 1e-6))

SGD的默认学习率为0.01

keras.optimizers.SGD(lr = 0.01,动量= 0.0,衰减= 0.0,nesterov = False)

我怀疑1e-6是小的,尝试增加它和/或尝试不同的优化器