实践中理解Tokenizer

Tokenizer

# Tokenization using DebertaV2Tokenizer
model_name = "/kaggle/input/qwen2/transformers/qwen2-7b-instruct/1"
# model_name = "/kaggle/input/deberta-v3/pytorch/large/1"
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Limit the vocabulary size
# tokenizer.model_max_length = max_length
tokenizer.add_tokens(['[CLS]', '[SEP]', '[PAD]'], special_tokens=True)
  • 如果在进行文本分类任务时,在输入文本的开头添加 [CLS] 令牌,模型可以学习识别这个令牌并根据其位置和相关特征进行分类判断
  • 在对不同文本段落进行拼接时,可以使用 [SEP] 来明确区分不同的部分
  • 而在对长度不一的文本序列进行处理时,使用 [PAD] 来使所有序列长度相同,便于模型进行统一处理。
# Check and set special tokens if they are not present
    if tokenizer.cls_token_id is None:
        tokenizer.cls_token_id = tokenizer.convert_tokens_to_ids('[CLS]')
    if tokenizer.sep_token_id is None:
        tokenizer.sep_token_id = tokenizer.convert_tokens_to_ids('[SEP]')
    if tokenizer.pad_token_id is None:
        tokenizer.pad_token_id = tokenizer.convert_tokens_to_ids('[PAD]')

如果 tokenizer 的 cls_token_id (表示 [CLS] 令牌的 ID)为空,就通过 tokenizer.convert_tokens_to_ids(‘[CLS]’) 这个方法将 [CLS] 令牌转换为对应的 ID 并赋值给 cls_token_id

这样做的目的是确保在后续的处理中,如果需要使用这些特殊令牌的 ID 时,它们已经被正确地定义和赋值,避免出现因为未定义而导致的错误

def process(row):
        max_len = max_length - 2 # 2 separator tokens
        # Tokenize prompt
        prompt_tokens = tokenizer(row['prompt'], truncation=True, max_length=max_len//4)['input_ids']
        remaining_length = max_len - len(prompt_tokens)

        # Tokenize response A
        response_a_tokens = tokenizer(row['response_a'], truncation=True, max_length=remaining_length//2)['input_ids']
        remaining_length -= len(response_a_tokens)

        # Tokenize response B
        response_b_tokens = tokenizer(row['response_b'], truncation=True, max_length=remaining_length//2)['input_ids']

        # Add responses
        input_ids = [tokenizer.cls_token_id] + prompt_tokens + [tokenizer.sep_token_id] + response_a_tokens + [tokenizer.sep_token_id] + response_b_tokens
        token_type_ids = [0] * (len(prompt_tokens) + 2) + [1] * (len(response_a_tokens) + 1) + [2] * len(response_b_tokens)
        attention_mask = [1] * len(input_ids)

        # Add padding
        padding_length = max_length - len(input_ids)
        if padding_length > 0:
            input_ids = input_ids + [tokenizer.pad_token_id] * padding_length
            token_type_ids = token_type_ids + [0] * padding_length
            attention_mask = attention_mask + [0] * padding_length

        input_ids = input_ids[:max_length]
        token_type_ids = token_type_ids[:max_length]
        attention_mask = attention_mask[:max_length]
        
        return input_ids, token_type_ids, attention_mask
    
    df[['input_ids', 'token_type_ids', 'attention_mask']] = df.apply(lambda row: pd.Series(process(row)), axis=1)

这段的核心是这句:

input_ids = [tokenizer.cls_token_id] + prompt_tokens + [tokenizer.sep_token_id] + response_a_tokens + [tokenizer.sep_token_id] + response_b_tokens

↑ 这些部分共同组成了编码后的句子

token_type_ids = [0] * (len(prompt_tokens) + 2) + [1] * (len(response_a_tokens) + 1) + [2] * len(response_b_tokens)

例如,如果 len(prompt_tokens) = 5 , len(response_a_tokens) = 3 , len(response_b_tokens) = 2 ,那么 token_type_ids 将会是 [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2] 。这种标记类型的区分在一些模型中可以帮助模型更好地理解输入序列中不同部分的含义和作用

attention_mask = [1] * len(input_ids)

全为 1 的 attention_mask 表示模型在处理输入序列 input_ids 时,应关注所有的位置。

# Prepare data for training
input_ids = train['input_ids']
attention_mask = train['attention_mask']

X_train = sq.pad_sequences(input_ids, maxlen=max_length)
X_train_attention_mask = sq.pad_sequences(attention_mask, maxlen=max_length)

y_train = labels

使用 sq.pad_sequences 函数对 input_ids 进行填充操作

为什么有一个X_train还有一个X_train_attention_mask?
——这样做的目的是为了让输入数据和对应的注意力掩码在长度上保持一致,以便后续能够正确地输入到模型中进行处理

Model

# Define the LSTM model
def create_lstm_model(vocab_size, embedding_dim, max_length):
    model = Sequential([
        Input(shape=(max_length,), dtype=tf.int32, name='input_ids'),
        Embedding(input_dim=vocab_size, output_dim=embedding_dim),
        LSTM(256, return_sequences=True),
#         BatchNormalization(),
#         Dropout(0.5),
        GlobalMaxPooling1D(),
        Dense(128, activation='relu'),
        Dropout(0.5),
        Dense(3, activation='softmax')
    ])
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model
# Parameters
vocab_size = tokenizer.vocab_size
# vocab_size = max_length
embedding_dim = 100
max_length = max_length
max_features = tokenizer.vocab_size
# max_features = max_length * 2
max_len = max_length
maxlen = max_len
batch_size = 16
embedding_dims = 100
nb_filter = 150
filter_length = 3
hidden_dims = 100
nb_epoch = 100
# Create the model
model = create_lstm_model(vocab_size, embedding_dim, max_length)
model.summary()

在这里插入图片描述

Training

from keras.callbacks import EarlyStopping

# Train the model
early_stopping = EarlyStopping(monitor='val_loss', patience=8, verbose=1)

history = model.fit([X_train, X_train_attention_mask], y_train, epochs=20, batch_size=32, validation_split=0.2
                    , callbacks=[early_stopping])

将 X 和 X_attention_mask 一起作为输入传递给模型,主要作用是为了让模型在进行预测时能够同时考虑输入的序列数据(X)以及关于每个位置是否需要关注的信息(X_attention_mask)。

X包含了实际要处理和预测的序列数据,例如文本的令牌 ID 序列。
而 X_attention_mask 用于指示模型在处理 X 时,哪些位置的信息是有效的、需要重点关注的,哪些位置的信息可能是填充的或者不太重要的,可以在计算中适当降低权重或者忽略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值