chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
# Copyright 2024 The Qwen Team and The HuggingFace Inc. team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .configuration import *
|
||||
from .modeling import *
|
||||
from .modeling_pp import *
|
||||
from .tokenizer import *
|
||||
from .tokenizer_fast import *
|
||||
@@ -0,0 +1,213 @@
|
||||
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
# Copyright 2024 The Qwen team, Alibaba Group and the HuggingFace Inc. team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Qwen2 model configuration"""
|
||||
|
||||
from ..configuration_utils import PretrainedConfig
|
||||
|
||||
|
||||
__all__ = [
|
||||
"QWEN2_PRETRAINED_INIT_CONFIGURATION",
|
||||
"Qwen2Config",
|
||||
"QWEN2_PRETRAINED_RESOURCE_FILES_MAP",
|
||||
]
|
||||
QWEN2_PRETRAINED_INIT_CONFIGURATION = {
|
||||
# Hypothetical model weights (tiny-random-llama & micro-random-llama) for test only
|
||||
"__internal_testing__/micro-random-llama": {
|
||||
"architectures": ["LlamaForCausalLM"],
|
||||
"hidden_size": 64,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 1000,
|
||||
"max_position_embeddings": 2048,
|
||||
"model_type": "llama",
|
||||
"num_attention_heads": 8,
|
||||
"num_hidden_layers": 1,
|
||||
"rms_norm_eps": 1e-06,
|
||||
"vocab_size": 32000,
|
||||
"bos_token_id": 1,
|
||||
"eos_token_id": 2,
|
||||
"pad_token_id": 0,
|
||||
},
|
||||
"__internal_testing__/tiny-random-llama": {
|
||||
"architectures": ["LlamaForCausalLM"],
|
||||
"hidden_size": 768,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 11008,
|
||||
"max_position_embeddings": 2048,
|
||||
"model_type": "llama",
|
||||
"num_attention_heads": 8,
|
||||
"num_hidden_layers": 2,
|
||||
"rms_norm_eps": 1e-06,
|
||||
"vocab_size": 32000,
|
||||
"bos_token_id": 1,
|
||||
"eos_token_id": 2,
|
||||
"pad_token_id": 0,
|
||||
},
|
||||
}
|
||||
|
||||
# Hypothetical model weights (tiny-random-llama) for test only
|
||||
QWEN2_PRETRAINED_RESOURCE_FILES_MAP = {
|
||||
"model_state": {
|
||||
"__internal_testing__/micro-random-llama": "https://bj.bcebos.com/paddlenlp/models/community/__internal_testing__/micro-random-llama/model_state.pdparams",
|
||||
"__internal_testing__/tiny-random-llama": "https://bj.bcebos.com/paddlenlp/models/community/__internal_testing__/tiny-random-llama/model_state.pdparams",
|
||||
},
|
||||
}
|
||||
|
||||
class Qwen2Config(PretrainedConfig):
|
||||
r"""
|
||||
This is the configuration class to store the configuration of a [`Qwen2Model`]. It is used to instantiate a
|
||||
Qwen2 model according to the specified arguments, defining the model architecture. Instantiating a configuration
|
||||
with the defaults will yield a similar configuration to that of
|
||||
Qwen2-7B-beta [Qwen/Qwen2-7B-beta](https://huggingface.co/Qwen/Qwen2-7B-beta).
|
||||
|
||||
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
||||
documentation from [`PretrainedConfig`] for more information.
|
||||
|
||||
|
||||
Args:
|
||||
vocab_size (`int`, *optional*, defaults to 151936):
|
||||
Vocabulary size of the Qwen2 model. Defines the number of different tokens that can be represented by the
|
||||
`inputs_ids` passed when calling [`Qwen2Model`]
|
||||
hidden_size (`int`, *optional*, defaults to 4096):
|
||||
Dimension of the hidden representations.
|
||||
intermediate_size (`int`, *optional*, defaults to 22016):
|
||||
Dimension of the MLP representations.
|
||||
num_hidden_layers (`int`, *optional*, defaults to 32):
|
||||
Number of hidden layers in the Transformer encoder.
|
||||
num_attention_heads (`int`, *optional*, defaults to 32):
|
||||
Number of attention heads for each attention layer in the Transformer encoder.
|
||||
num_key_value_heads (`int`, *optional*, defaults to 32):
|
||||
This is the number of key_value heads that should be used to implement Grouped Query Attention. If
|
||||
`num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
|
||||
`num_key_value_heads=1 the model will use Multi Query Attention (MQA) otherwise GQA is used. When
|
||||
converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
|
||||
by meanpooling all the original heads within that group. For more details checkout [this
|
||||
paper](https://arxiv.org/pdf/2305.13245.pdf). If it is not specified, will default to `32`.
|
||||
hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
|
||||
The non-linear activation function (function or string) in the decoder.
|
||||
max_position_embeddings (`int`, *optional*, defaults to 32768):
|
||||
The maximum sequence length that this model might ever be used with.
|
||||
initializer_range (`float`, *optional*, defaults to 0.02):
|
||||
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
||||
rms_norm_eps (`float`, *optional*, defaults to 1e-06):
|
||||
The epsilon used by the rms normalization layers.
|
||||
use_cache (`bool`, *optional*, defaults to `True`):
|
||||
Whether or not the model should return the last key/values attentions (not used by all models). Only
|
||||
relevant if `config.is_decoder=True`.
|
||||
tie_word_embeddings (`bool`, *optional*, defaults to `False`):
|
||||
Whether the model's input and output word embeddings should be tied.
|
||||
rope_theta (`float`, *optional*, defaults to 10000.0):
|
||||
The base period of the RoPE embeddings.
|
||||
use_sliding_window (`bool`, *optional*, defaults to `False`):
|
||||
Whether to use sliding window attention.
|
||||
sliding_window (`int`, *optional*, defaults to 4096):
|
||||
Sliding window attention (SWA) window size. If not specified, will default to `4096`.
|
||||
max_window_layers (`int`, *optional*, defaults to 28):
|
||||
The number of layers that use SWA (Sliding Window Attention). The bottom layers use SWA while the top use full attention.
|
||||
attention_dropout (`float`, *optional*, defaults to 0.0):
|
||||
The dropout ratio for the attention probabilities.
|
||||
|
||||
```python
|
||||
>>> from transformers import Qwen2Model, Qwen2Config
|
||||
|
||||
>>> # Initializing a Qwen2 style configuration
|
||||
>>> configuration = Qwen2Config()
|
||||
|
||||
>>> # Initializing a model from the Qwen2-7B style configuration
|
||||
>>> model = Qwen2Model(configuration)
|
||||
|
||||
>>> # Accessing the model configuration
|
||||
>>> configuration = model.config
|
||||
```"""
|
||||
|
||||
model_type = "qwen2"
|
||||
keys_to_ignore_at_inference = ["past_key_values"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vocab_size=151936,
|
||||
hidden_size=4096,
|
||||
intermediate_size=22016,
|
||||
num_hidden_layers=32,
|
||||
num_attention_heads=32,
|
||||
num_key_value_heads=32,
|
||||
hidden_act="silu",
|
||||
max_position_embeddings=32768,
|
||||
# seq_length=32768,
|
||||
initializer_range=0.02,
|
||||
rms_norm_eps=1e-6,
|
||||
use_cache=True,
|
||||
tie_word_embeddings=False,
|
||||
rope_theta=10000.0,
|
||||
pad_token_id=151643,
|
||||
bos_token_id=151643,
|
||||
eos_token_id=151643,
|
||||
use_sliding_window=False,
|
||||
sliding_window=4096,
|
||||
max_window_layers=28,
|
||||
use_flash_attention_for_generation=False,
|
||||
alibi=False,
|
||||
use_last_token_for_generation=False,
|
||||
attention_bias=True,
|
||||
attention_dropout=0.0,
|
||||
rope_scaling_factor=1.0,
|
||||
rope_scaling_type=None,
|
||||
dpo_config=None,
|
||||
use_fused_head_and_loss_fn=False,
|
||||
**kwargs,
|
||||
):
|
||||
self.vocab_size = vocab_size
|
||||
self.max_position_embeddings = max_position_embeddings
|
||||
# self.seq_length = seq_length
|
||||
self.hidden_size = hidden_size
|
||||
self.intermediate_size = intermediate_size
|
||||
self.num_hidden_layers = num_hidden_layers
|
||||
self.num_attention_heads = num_attention_heads
|
||||
self.use_sliding_window = use_sliding_window
|
||||
self.sliding_window = sliding_window
|
||||
self.max_window_layers = max_window_layers
|
||||
|
||||
# for backward compatibility
|
||||
if num_key_value_heads is None:
|
||||
num_key_value_heads = num_attention_heads
|
||||
|
||||
self.num_key_value_heads = num_key_value_heads
|
||||
self.hidden_act = hidden_act
|
||||
self.initializer_range = initializer_range
|
||||
self.rms_norm_eps = rms_norm_eps
|
||||
self.use_cache = use_cache
|
||||
self.rope_theta = rope_theta
|
||||
self.attention_bias = attention_bias
|
||||
self.attention_dropout = attention_dropout
|
||||
|
||||
self.use_cache = use_cache
|
||||
self.rope_scaling_factor = rope_scaling_factor
|
||||
self.rope_scaling_type = rope_scaling_type
|
||||
|
||||
self.pad_token_id = pad_token_id
|
||||
self.bos_token_id = bos_token_id
|
||||
self.eos_token_id = eos_token_id
|
||||
self.dpo_config = dpo_config
|
||||
self.use_flash_attention_for_generation = use_flash_attention_for_generation
|
||||
self.alibi = alibi
|
||||
self.use_fused_head_and_loss_fn = use_fused_head_and_loss_fn
|
||||
self.use_last_token_for_generation = use_last_token_for_generation
|
||||
|
||||
super().__init__(
|
||||
pad_token_id=pad_token_id,
|
||||
bos_token_id=bos_token_id,
|
||||
eos_token_id=eos_token_id,
|
||||
tie_word_embeddings=tie_word_embeddings,
|
||||
**kwargs,
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,364 @@
|
||||
# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from typing import OrderedDict
|
||||
|
||||
import paddle
|
||||
import paddle.distributed.fleet as fleet
|
||||
import paddle.nn as nn
|
||||
from paddle.distributed.fleet.meta_parallel import (
|
||||
LayerDesc,
|
||||
PipelineLayer,
|
||||
SharedLayerDesc,
|
||||
)
|
||||
from paddle.distributed.fleet.recompute.recompute import recompute
|
||||
|
||||
from paddlenlp.transformers.refined_recompute import get_skip_recompute_ops
|
||||
from paddlenlp.transformers.refined_recompute import recompute as rr_recompute
|
||||
|
||||
from ...utils.tools import get_env_device
|
||||
from ..dpo_criterion import DPOCriterion
|
||||
from ..model_utils import PipelinePretrainedModel
|
||||
from .modeling import (
|
||||
Qwen2Config,
|
||||
Qwen2DecoderLayer,
|
||||
Qwen2LMHead,
|
||||
Qwen2Model,
|
||||
Qwen2PretrainedModel,
|
||||
Qwen2PretrainingCriterion,
|
||||
Qwen2RMSNorm,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Qwen2ForCausalLMPipe",
|
||||
]
|
||||
|
||||
|
||||
def parse_args(args):
|
||||
if isinstance(args, tuple):
|
||||
if len(args) == 4:
|
||||
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = args
|
||||
elif len(args) == 3:
|
||||
hidden_states, attention_mask, attn_mask_startend_row_indices = args
|
||||
position_ids = None
|
||||
elif len(args) == 2:
|
||||
hidden_states, attention_mask = args
|
||||
attn_mask_startend_row_indices, position_ids = None, None
|
||||
else:
|
||||
hidden_states = args
|
||||
attention_mask, attn_mask_startend_row_indices, position_ids = None, None, None
|
||||
|
||||
if position_ids is not None:
|
||||
position_ids.stop_gradient = True
|
||||
|
||||
if attention_mask is not None:
|
||||
attention_mask.stop_gradient = True
|
||||
|
||||
if attn_mask_startend_row_indices is not None:
|
||||
attn_mask_startend_row_indices.stop_gradient = True
|
||||
|
||||
return hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids
|
||||
|
||||
|
||||
def return_args(hidden_states, attention_mask=None, attn_mask_startend_row_indices=None, position_ids=None):
|
||||
ret = (hidden_states,)
|
||||
|
||||
if attention_mask is not None:
|
||||
ret += (attention_mask.clone(),)
|
||||
if attn_mask_startend_row_indices is not None:
|
||||
ret += (attn_mask_startend_row_indices.clone(),)
|
||||
if position_ids is not None:
|
||||
ret += (position_ids.clone(),)
|
||||
if len(ret) == 1:
|
||||
ret = ret[0]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_attr(layer, name):
|
||||
if getattr(layer, name, None) is not None:
|
||||
return getattr(layer, name, None)
|
||||
else:
|
||||
return get_attr(layer._layer, name)
|
||||
|
||||
|
||||
class Qwen2EmbeddingPipe(nn.Layer):
|
||||
"""Extends QWenEmbeddings to forward attention_mask through the pipeline."""
|
||||
|
||||
def __init__(self, config: Qwen2Config):
|
||||
super(Qwen2EmbeddingPipe, self).__init__()
|
||||
self.config = config
|
||||
self.sequence_parallel = config.sequence_parallel
|
||||
self.hidden_size = config.hidden_size
|
||||
if config.tensor_parallel_degree > 1 and config.vocab_size % config.tensor_parallel_degree == 0:
|
||||
self.embed_tokens = fleet.meta_parallel.VocabParallelEmbedding(
|
||||
config.vocab_size,
|
||||
config.hidden_size,
|
||||
weight_attr=paddle.ParamAttr(initializer=nn.initializer.XavierNormal()),
|
||||
)
|
||||
else:
|
||||
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
|
||||
|
||||
@property
|
||||
def embedding_weight(self):
|
||||
return get_attr(self.embed_tokens, "weight")
|
||||
|
||||
def forward(self, args):
|
||||
"""_summary_
|
||||
|
||||
Args:
|
||||
input (_type_): _description_
|
||||
|
||||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
input_ids, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
|
||||
input_embeds = self.embed_tokens(input_ids)
|
||||
if self.config.sequence_parallel:
|
||||
from paddlenlp.transformers import ScatterOp
|
||||
|
||||
# [bs, seq_len, num_head * head_dim] -> [bs * seq_len, num_head * head_dim]
|
||||
bs, seq_len, hidden_size = input_embeds.shape
|
||||
input_embeds = paddle.reshape_(input_embeds, [bs * seq_len, hidden_size])
|
||||
# [seq_len * bs / n, num_head * head_dim] (n is mp parallelism)
|
||||
input_embeds = ScatterOp.apply(input_embeds)
|
||||
|
||||
batch_size, seq_length = input_ids.shape
|
||||
|
||||
if attention_mask is not None:
|
||||
assert (
|
||||
attn_mask_startend_row_indices is None
|
||||
), "attention_mask and attn_mask_startend_row_indices can not be set at same time"
|
||||
|
||||
attention_mask = Qwen2Model._prepare_decoder_attention_mask(
|
||||
attention_mask, (batch_size, seq_length), 0, input_embeds.dtype
|
||||
)
|
||||
attention_mask.stop_gradient = True
|
||||
if get_env_device() == "npu":
|
||||
attention_mask = attention_mask.astype("bool")
|
||||
elif get_env_device() == "npu":
|
||||
attention_mask = paddle.tril(paddle.ones((seq_length, seq_length), dtype="bool"))
|
||||
attention_mask.stop_gradient = True
|
||||
|
||||
return return_args(input_embeds, attention_mask, attn_mask_startend_row_indices, position_ids)
|
||||
|
||||
|
||||
class Qwen2DecoderLayerPipe(Qwen2DecoderLayer):
|
||||
def forward(self, args):
|
||||
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
|
||||
|
||||
has_gradient = not hidden_states.stop_gradient
|
||||
|
||||
if attention_mask is not None and attention_mask.dtype == paddle.int32:
|
||||
attention_mask, attn_mask_startend_row_indices, position_ids = (
|
||||
None,
|
||||
attention_mask,
|
||||
attn_mask_startend_row_indices,
|
||||
)
|
||||
elif attention_mask is not None and attention_mask.dtype == paddle.int64:
|
||||
attention_mask, attn_mask_startend_row_indices, position_ids = None, None, attention_mask
|
||||
elif attn_mask_startend_row_indices is not None and attn_mask_startend_row_indices.dtype == paddle.int64:
|
||||
attn_mask_startend_row_indices, position_ids = None, attn_mask_startend_row_indices
|
||||
|
||||
if self.enable_recompute and self.config.recompute_granularity == "full" and has_gradient:
|
||||
recompute_fn = rr_recompute if any(self.skip_recompute_ops.values()) else recompute
|
||||
if attention_mask is not None or attn_mask_startend_row_indices is not None:
|
||||
hidden_states = recompute_fn(
|
||||
super().forward,
|
||||
hidden_states,
|
||||
position_ids=position_ids,
|
||||
attention_mask=attention_mask,
|
||||
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
|
||||
use_reentrant=False,
|
||||
)
|
||||
else:
|
||||
# for pretrain
|
||||
hidden_states = recompute_fn(
|
||||
super().forward,
|
||||
hidden_states,
|
||||
position_ids=position_ids,
|
||||
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
|
||||
use_reentrant=self.config.recompute_use_reentrant,
|
||||
)
|
||||
else:
|
||||
hidden_states = super().forward(
|
||||
hidden_states,
|
||||
position_ids=position_ids,
|
||||
attention_mask=attention_mask,
|
||||
attn_mask_startend_row_indices=attn_mask_startend_row_indices,
|
||||
)
|
||||
|
||||
return return_args(hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids)
|
||||
|
||||
|
||||
class Qwen2RMSNormPipe(nn.Layer):
|
||||
def __init__(self, config):
|
||||
super().__init__()
|
||||
self.norm = Qwen2RMSNorm(config)
|
||||
|
||||
def forward(self, args):
|
||||
hidden_states, attention_mask, attn_mask_startend_row_indices, position_ids = parse_args(args)
|
||||
return self.norm(hidden_states)
|
||||
|
||||
|
||||
class Qwen2LMHeadPipe(Qwen2LMHead):
|
||||
def __init__(self, config, transpose_y=False):
|
||||
super(Qwen2LMHeadPipe, self).__init__(config, transpose_y=transpose_y)
|
||||
|
||||
@property
|
||||
def embedding_weight(self):
|
||||
return get_attr(self, "weight")
|
||||
|
||||
|
||||
class Qwen2ForCausalLMPipe(PipelinePretrainedModel, PipelineLayer):
|
||||
"""QWenForPretraining adapted for pipeline parallelism.
|
||||
|
||||
The largest change is flattening the QWenModel class so we can express it as a
|
||||
sequence of layers including embedding, transformer layers, and output.
|
||||
"""
|
||||
|
||||
config_class = Qwen2Config
|
||||
|
||||
_get_tensor_parallel_mappings = Qwen2PretrainedModel._get_tensor_parallel_mappings
|
||||
_init_weights = Qwen2PretrainedModel._init_weights
|
||||
_keys_to_ignore_on_load_unexpected = Qwen2PretrainedModel._keys_to_ignore_on_load_unexpected
|
||||
_get_model_flops = Qwen2PretrainedModel._get_model_flops
|
||||
_get_hardware_flops = Qwen2PretrainedModel._get_hardware_flops
|
||||
|
||||
_tied_weights_keys = ["lm_head.weight"]
|
||||
|
||||
# DONOT Add base_model_prefix !!!!
|
||||
|
||||
@classmethod
|
||||
def _prepare_pipeline_inputs_func(cls, inputs):
|
||||
|
||||
first_stage_keys = ["input_ids", "attention_mask", "attn_mask_startend_row_indices", "position_ids"]
|
||||
last_stage_keys = ["labels"]
|
||||
|
||||
def get_expected_keys(inputs, keys):
|
||||
ret = tuple([inputs.pop(k) if k in inputs else None for k in keys])
|
||||
if len(ret) == 1:
|
||||
ret = ret[0]
|
||||
return ret
|
||||
|
||||
if type(inputs) is dict or type(inputs) is OrderedDict:
|
||||
return [
|
||||
get_expected_keys(inputs, first_stage_keys),
|
||||
get_expected_keys(inputs, last_stage_keys),
|
||||
]
|
||||
|
||||
keys = list(inputs[0].keys())
|
||||
inputs_batch = {key: [data.pop(key) for data in inputs] for key in keys}
|
||||
return [
|
||||
get_expected_keys(inputs_batch, first_stage_keys),
|
||||
get_expected_keys(inputs_batch, last_stage_keys),
|
||||
]
|
||||
|
||||
def __init__(self, config: Qwen2Config):
|
||||
self.config = config
|
||||
|
||||
# Note that we will actually perform a recompute only if both enable_recompute and layerwise_recompute are set to True
|
||||
# Enable_recompute defaults to False and is controlled by Trainer
|
||||
self.enable_recompute = False
|
||||
self.recompute_granularity = self.config.recompute_granularity
|
||||
self.pp_recompute_interval = self.config.pp_recompute_interval
|
||||
self.no_recompute_layers = config.no_recompute_layers if config.no_recompute_layers is not None else []
|
||||
if self.recompute_granularity == "full":
|
||||
assert len(self.no_recompute_layers) == 0, "for pp with full recompute, no_recompute_layers is not support"
|
||||
|
||||
virtual_pp_degree = getattr(self.config, "virtual_pp_degree", 1)
|
||||
|
||||
def get_hcg():
|
||||
return fleet.get_hybrid_communicate_group()
|
||||
|
||||
hcg = get_hcg()
|
||||
tensor_parallel_degree = max(hcg.get_model_parallel_world_size(), 1)
|
||||
tensor_parallel_rank = max(hcg.get_model_parallel_rank(), 0)
|
||||
|
||||
# TODO: fix tensor_parallel_degree rewrite in here
|
||||
config.tensor_parallel_degree = tensor_parallel_degree
|
||||
config.tensor_parallel_rank = tensor_parallel_rank
|
||||
|
||||
if config.tie_word_embeddings:
|
||||
self.add_sequential_layer(
|
||||
SharedLayerDesc(
|
||||
"qwen2_shared_weight", Qwen2EmbeddingPipe, shared_weight_attr="embedding_weight", config=config
|
||||
),
|
||||
"qwen2",
|
||||
)
|
||||
else:
|
||||
self.add_sequential_layer(LayerDesc(Qwen2EmbeddingPipe, config=config), "qwen2")
|
||||
|
||||
for i in range(config.num_hidden_layers):
|
||||
self.add_sequential_layer(
|
||||
LayerDesc(
|
||||
Qwen2DecoderLayerPipe,
|
||||
config=config,
|
||||
layerwise_recompute=i not in self.no_recompute_layers,
|
||||
skip_recompute_ops=get_skip_recompute_ops(config, i),
|
||||
),
|
||||
f"qwen2.layers.{i}",
|
||||
)
|
||||
self.add_sequential_layer(LayerDesc(Qwen2RMSNormPipe, config=config), "qwen2")
|
||||
|
||||
if config.tie_word_embeddings:
|
||||
self.add_sequential_layer(
|
||||
SharedLayerDesc(
|
||||
"qwen2_shared_weight",
|
||||
Qwen2LMHeadPipe,
|
||||
shared_weight_attr="embedding_weight",
|
||||
config=config,
|
||||
**{"transpose_y": True},
|
||||
),
|
||||
"lm_head",
|
||||
)
|
||||
else:
|
||||
self.add_sequential_layer(LayerDesc(Qwen2LMHeadPipe, config=config), "lm_head")
|
||||
|
||||
recompute_interval = 0
|
||||
if self.enable_recompute and self.recompute_granularity == "full":
|
||||
assert self.config.pp_recompute_interval <= config.num_hidden_layers // (
|
||||
virtual_pp_degree * get_hcg().topology().get_dim_size("pipe")
|
||||
), "pp recompute interval should smaller than num layers of each pp chunk"
|
||||
recompute_interval = self.config.pp_recompute_interval
|
||||
|
||||
seg_method = "layer:Qwen2DecoderLayer"
|
||||
if config.num_hidden_layers % get_hcg().topology().get_dim_size("pipe") != 0:
|
||||
seg_method = "uniform"
|
||||
|
||||
PipelineLayer.__init__(
|
||||
self,
|
||||
layers=self.get_sequential_layers(),
|
||||
loss_fn=self.get_loss_fn(config),
|
||||
topology=get_hcg().topology(),
|
||||
seg_method=seg_method,
|
||||
recompute_interval=recompute_interval,
|
||||
recompute_ctx={
|
||||
"mp_group": get_hcg().get_model_parallel_group(),
|
||||
"offload": False,
|
||||
"partition": False,
|
||||
},
|
||||
num_virtual_pipeline_stages=virtual_pp_degree,
|
||||
)
|
||||
# You should call init here, since there is a diamond inheritance problem
|
||||
self.apply(self._init_weights)
|
||||
# DON'T init PipelinePretrainedModel
|
||||
# PipelinePretrainedModel.__init__(self.super(), config=config)
|
||||
|
||||
def get_loss_fn(self, config):
|
||||
if config.dpo_config is not None:
|
||||
return DPOCriterion(config, use_infohub=True)
|
||||
else:
|
||||
return Qwen2PretrainingCriterion(config)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,448 @@
|
||||
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
# Copyright 2024 The Qwen team, Alibaba Group and The HuggingFace Inc. team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Tokenization classes for Qwen2."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import unicodedata
|
||||
from functools import lru_cache
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import regex as re
|
||||
|
||||
from ...utils.log import logger
|
||||
from .. import AddedToken, PretrainedTokenizer
|
||||
|
||||
VOCAB_FILES_NAMES = {
|
||||
"vocab_file": "vocab.json",
|
||||
"merges_file": "merges.txt",
|
||||
}
|
||||
|
||||
__all__ = ["Qwen2Tokenizer"]
|
||||
|
||||
MAX_MODEL_INPUT_SIZES = {"__internal_testing__/tiny-random-qwen2": 32768}
|
||||
|
||||
PRETOKENIZE_REGEX = r"""(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+"""
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def bytes_to_unicode():
|
||||
"""
|
||||
Returns list of utf-8 byte and a mapping to unicode strings. We specifically avoids mapping to whitespace/control
|
||||
characters the bpe code barfs on.
|
||||
|
||||
The reversible bpe codes work on unicode strings. This means you need a large # of unicode characters in your vocab
|
||||
if you want to avoid UNKs. When you're at something like a 10B token dataset you end up needing around 5K for
|
||||
decent coverage. This is a significant percentage of your normal, say, 32K bpe vocab. To avoid that, we want lookup
|
||||
tables between utf-8 bytes and unicode strings.
|
||||
"""
|
||||
bs = (
|
||||
list(range(ord("!"), ord("~") + 1)) + list(range(ord("¡"), ord("¬") + 1)) + list(range(ord("®"), ord("ÿ") + 1))
|
||||
)
|
||||
cs = bs[:]
|
||||
n = 0
|
||||
for b in range(2**8):
|
||||
if b not in bs:
|
||||
bs.append(b)
|
||||
cs.append(2**8 + n)
|
||||
n += 1
|
||||
cs = [chr(n) for n in cs]
|
||||
return dict(zip(bs, cs))
|
||||
|
||||
|
||||
def get_pairs(word):
|
||||
"""
|
||||
Return set of symbol pairs in a word.
|
||||
|
||||
Word is represented as tuple of symbols (symbols being variable-length strings).
|
||||
"""
|
||||
pairs = set()
|
||||
prev_char = word[0]
|
||||
for char in word[1:]:
|
||||
pairs.add((prev_char, char))
|
||||
prev_char = char
|
||||
return pairs
|
||||
|
||||
|
||||
class Qwen2Tokenizer(PretrainedTokenizer):
|
||||
"""
|
||||
Construct a Qwen2 tokenizer. Based on byte-level Byte-Pair-Encoding.
|
||||
|
||||
Same with GPT2Tokenizer, this tokenizer has been trained to treat spaces like parts of the tokens so a word will
|
||||
be encoded differently whether it is at the beginning of the sentence (without space) or not:
|
||||
|
||||
```python
|
||||
>>> from transformers import Qwen2Tokenizer
|
||||
|
||||
>>> tokenizer = Qwen2Tokenizer.from_pretrained("Qwen/Qwen-tokenizer")
|
||||
>>> tokenizer("Hello world")["input_ids"]
|
||||
[9707, 1879]
|
||||
|
||||
>>> tokenizer(" Hello world")["input_ids"]
|
||||
[21927, 1879]
|
||||
```
|
||||
This is expected.
|
||||
|
||||
You should not use GPT2Tokenizer instead, because of the different pretokenization rules.
|
||||
|
||||
This tokenizer inherits from [`PreTrainedTokenizer`] which contains most of the main methods. Users should refer to
|
||||
this superclass for more information regarding those methods.
|
||||
|
||||
Args:
|
||||
vocab_file (`str`):
|
||||
Path to the vocabulary file.
|
||||
merges_file (`str`):
|
||||
Path to the merges file.
|
||||
errors (`str`, *optional*, defaults to `"replace"`):
|
||||
Paradigm to follow when decoding bytes to UTF-8. See
|
||||
[bytes.decode](https://docs.python.org/3/library/stdtypes.html#bytes.decode) for more information.
|
||||
unk_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
|
||||
The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
|
||||
token instead.
|
||||
bos_token (`str`, *optional*):
|
||||
The beginning of sequence token. Not applicable for this tokenizer.
|
||||
eos_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
|
||||
The end of sequence token.
|
||||
pad_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
|
||||
The token used for padding, for example when batching sequences of different lengths.
|
||||
clean_up_tokenization_spaces (`bool`, *optional*, defaults to `False`):
|
||||
Whether or not the model should cleanup the spaces that were added when splitting the input text during the
|
||||
tokenization process. Not applicable to this tokenizer, since tokenization does not add spaces.
|
||||
split_special_tokens (`bool`, *optional*, defaults to `False`):
|
||||
Whether or not the special tokens should be split during the tokenization process. The default behavior is
|
||||
to not split special tokens. This means that if `<|endoftext|>` is the `eos_token`, then `tokenizer.tokenize("<|endoftext|>") =
|
||||
['<|endoftext|>`]. Otherwise, if `split_special_tokens=True`, then `tokenizer.tokenize("<|endoftext|>")` will be give `['<',
|
||||
'|', 'endo', 'ft', 'ext', '|', '>']`. This argument is only supported for `slow` tokenizers for the moment.
|
||||
"""
|
||||
|
||||
resource_files_names = VOCAB_FILES_NAMES
|
||||
model_input_names = ["input_ids", "attention_mask", "attn_mask_startend_row_indices"]
|
||||
max_model_input_sizes = MAX_MODEL_INPUT_SIZES
|
||||
|
||||
pretrained_resource_files_map = {
|
||||
"vocab_file": {
|
||||
"__internal_testing__/tiny-random-qwen2": "https://bj.bcebos.com/paddlenlp/models/community/qwen2/vocab.json",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vocab_file,
|
||||
merges_file,
|
||||
errors="replace",
|
||||
unk_token="<|endoftext|>",
|
||||
bos_token=None,
|
||||
eos_token="<|endoftext|>",
|
||||
pad_token="<|endoftext|>",
|
||||
clean_up_tokenization_spaces=False,
|
||||
split_special_tokens=False,
|
||||
**kwargs,
|
||||
):
|
||||
|
||||
if unk_token is None:
|
||||
logger.info("The `unk_token` parameter needs to be defined: we use `eos_token` by default.")
|
||||
unk_token = eos_token
|
||||
|
||||
# Qwen vocab does not contain control tokens; added tokens need to be special
|
||||
bos_token = (
|
||||
AddedToken(bos_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(bos_token, str)
|
||||
else bos_token
|
||||
)
|
||||
eos_token = (
|
||||
AddedToken(eos_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(eos_token, str)
|
||||
else eos_token
|
||||
)
|
||||
unk_token = (
|
||||
AddedToken(unk_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(unk_token, str)
|
||||
else unk_token
|
||||
)
|
||||
pad_token = (
|
||||
AddedToken(pad_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(pad_token, str)
|
||||
else pad_token
|
||||
)
|
||||
|
||||
with open(vocab_file, encoding="utf-8") as vocab_handle:
|
||||
self.encoder = json.load(vocab_handle)
|
||||
self.decoder = {v: k for k, v in self.encoder.items()}
|
||||
self.errors = errors # how to handle errors in decoding
|
||||
self.byte_encoder = bytes_to_unicode()
|
||||
self.byte_decoder = {v: k for k, v in self.byte_encoder.items()}
|
||||
bpe_merges = []
|
||||
with open(merges_file, encoding="utf-8") as merges_handle:
|
||||
for i, line in enumerate(merges_handle):
|
||||
line = line.strip()
|
||||
if (i == 0 and line.startswith("#version:")) or not line:
|
||||
continue
|
||||
bpe_merges.append(tuple(line.split()))
|
||||
self.bpe_ranks = dict(zip(bpe_merges, range(len(bpe_merges))))
|
||||
# NOTE: the cache can grow without bound and will get really large for long running processes
|
||||
# (esp. for texts of language that do not use space between word, e.g. Chinese); technically
|
||||
# not a memory leak but appears as one.
|
||||
# GPT2Tokenizer has the same problem, so let's be consistent.
|
||||
self.cache = {}
|
||||
|
||||
self.pat = re.compile(PRETOKENIZE_REGEX)
|
||||
|
||||
if kwargs.get("add_prefix_space", False):
|
||||
logger.warning_once(
|
||||
f"{self.__class__.__name} does not support `add_prefix_space`, setting it to True has no effect."
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
errors=errors,
|
||||
bos_token=bos_token,
|
||||
eos_token=eos_token,
|
||||
pad_token=pad_token,
|
||||
unk_token=unk_token,
|
||||
clean_up_tokenization_spaces=clean_up_tokenization_spaces,
|
||||
split_special_tokens=split_special_tokens,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@property
|
||||
def vocab_size(self) -> int:
|
||||
return len(self.encoder)
|
||||
|
||||
def get_vocab(self):
|
||||
return dict(self.encoder, **self.added_tokens_encoder)
|
||||
|
||||
def bpe(self, token):
|
||||
if token in self.cache:
|
||||
return self.cache[token]
|
||||
word = tuple(token)
|
||||
pairs = get_pairs(word)
|
||||
|
||||
if not pairs:
|
||||
return token
|
||||
|
||||
while True:
|
||||
bigram = min(pairs, key=lambda pair: self.bpe_ranks.get(pair, float("inf")))
|
||||
if bigram not in self.bpe_ranks:
|
||||
break
|
||||
first, second = bigram
|
||||
new_word = []
|
||||
i = 0
|
||||
while i < len(word):
|
||||
try:
|
||||
j = word.index(first, i)
|
||||
except ValueError:
|
||||
new_word.extend(word[i:])
|
||||
break
|
||||
else:
|
||||
new_word.extend(word[i:j])
|
||||
i = j
|
||||
|
||||
if word[i] == first and i < len(word) - 1 and word[i + 1] == second:
|
||||
new_word.append(first + second)
|
||||
i += 2
|
||||
else:
|
||||
new_word.append(word[i])
|
||||
i += 1
|
||||
new_word = tuple(new_word)
|
||||
word = new_word
|
||||
if len(word) == 1:
|
||||
break
|
||||
else:
|
||||
pairs = get_pairs(word)
|
||||
word = " ".join(word)
|
||||
self.cache[token] = word
|
||||
return word
|
||||
|
||||
def _tokenize(self, text):
|
||||
"""Tokenize a string."""
|
||||
bpe_tokens = []
|
||||
for token in re.findall(self.pat, text):
|
||||
token = "".join(
|
||||
self.byte_encoder[b] for b in token.encode("utf-8")
|
||||
) # Maps all our bytes to unicode strings, avoiding control tokens of the BPE (spaces in our case)
|
||||
bpe_tokens.extend(bpe_token for bpe_token in self.bpe(token).split(" "))
|
||||
return bpe_tokens
|
||||
|
||||
def _convert_token_to_id(self, token):
|
||||
"""Converts a token (str) in an id using the vocab."""
|
||||
return self.encoder.get(token, self.added_tokens_encoder.get(token, len(self.encoder)))
|
||||
|
||||
def _convert_id_to_token(self, index):
|
||||
"""Converts an index (integer) in a token (str) using the vocab."""
|
||||
return self.decoder.get(index, self.added_tokens_decoder.get(index, self.unk_token))
|
||||
|
||||
def convert_tokens_to_string(self, tokens):
|
||||
"""Converts a sequence of tokens (string) in a single string."""
|
||||
text = "".join(tokens)
|
||||
text = bytearray([self.byte_decoder[c] for c in text]).decode("utf-8", errors=self.errors)
|
||||
return text
|
||||
|
||||
def _decode(
|
||||
self,
|
||||
token_ids,
|
||||
skip_special_tokens: bool = False,
|
||||
clean_up_tokenization_spaces: Optional[bool] = False,
|
||||
spaces_between_special_tokens: bool = False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
# `spaces_between_special_tokens` defaults to True for _decode in slow tokenizers
|
||||
# and cannot be configured elsewhere, but it should default to False for Qwen2Tokenizer
|
||||
return super()._decode(
|
||||
token_ids,
|
||||
skip_special_tokens=skip_special_tokens,
|
||||
clean_up_tokenization_spaces=clean_up_tokenization_spaces,
|
||||
spaces_between_special_tokens=spaces_between_special_tokens,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:
|
||||
if not os.path.isdir(save_directory):
|
||||
logger.error(f"Vocabulary path ({save_directory}) should be a directory")
|
||||
return
|
||||
vocab_file = os.path.join(
|
||||
save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]
|
||||
)
|
||||
merge_file = os.path.join(
|
||||
save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["merges_file"]
|
||||
)
|
||||
|
||||
with open(vocab_file, "w", encoding="utf-8") as f:
|
||||
f.write(json.dumps(self.encoder, indent=2, sort_keys=True, ensure_ascii=False) + "\n")
|
||||
|
||||
index = 0
|
||||
with open(merge_file, "w", encoding="utf-8") as writer:
|
||||
writer.write("#version: 0.2\n")
|
||||
for bpe_tokens, token_index in sorted(self.bpe_ranks.items(), key=lambda kv: kv[1]):
|
||||
if index != token_index:
|
||||
logger.warning(
|
||||
f"Saving vocabulary to {merge_file}: BPE merge indices are not consecutive."
|
||||
" Please check that the tokenizer is not corrupted!"
|
||||
)
|
||||
index = token_index
|
||||
writer.write(" ".join(bpe_tokens) + "\n")
|
||||
index += 1
|
||||
|
||||
return vocab_file, merge_file
|
||||
|
||||
def prepare_for_tokenization(self, text, **kwargs):
|
||||
text = unicodedata.normalize("NFC", text)
|
||||
return (text, kwargs)
|
||||
|
||||
def _encode_chat_inputs(
|
||||
self,
|
||||
conversations: List[List[str, str]],
|
||||
context_data: Dict[str, Any] = {},
|
||||
system: str = None,
|
||||
add_generation_prompt=True,
|
||||
):
|
||||
result = {}
|
||||
|
||||
# Some template do not support system msg, so we need to check it first.
|
||||
if system:
|
||||
try:
|
||||
self.chat_template.render(messages={"role": "system", "content": system})
|
||||
except Exception as e:
|
||||
raise ValueError("System is not supported in this tokenizer.", e)
|
||||
|
||||
# convert list msg to role dict msg
|
||||
conversation_dict = []
|
||||
origin_msg = []
|
||||
for round in conversations:
|
||||
round_role = [
|
||||
{"role": "user", "content": round[0]},
|
||||
{"role": "assistant", "content": round[1]},
|
||||
]
|
||||
origin_msg.extend(round_role)
|
||||
conversation_dict.append(round_role)
|
||||
|
||||
# Get system string in ChatTemplate
|
||||
# ChatTemplate contains three parts: system, user, and assistant.
|
||||
# However, the system string cannot be obtained directly with the chat_template.render() function.
|
||||
# Thus, three steps are needed to extract the system string.
|
||||
# Step 1: Obtain the combined system and user string in the first round.
|
||||
# Step 2: Obtain the special system string.
|
||||
# Step 3: Obtain the special combined system and user string in the first round.
|
||||
# Then, user string = (special system and user string) - (special system string)
|
||||
# And, system string = (initial system and user string) - (user string)
|
||||
|
||||
assert len(conversation_dict) > 0, "conversations is empty"
|
||||
|
||||
def replace_first_occurrence(original_string, to_find, to_replace):
|
||||
index = original_string.find(to_find)
|
||||
if index == -1: # to_find not found in original_string
|
||||
return original_string
|
||||
else:
|
||||
return original_string[:index] + to_replace + original_string[index + len(to_find) :]
|
||||
|
||||
if system:
|
||||
system_str = self.chat_template.render([system])
|
||||
else:
|
||||
# get system and user str
|
||||
round0_str = self.chat_template.render(
|
||||
messages=conversation_dict[0][:1], add_generation_prompt=False, **self.special_tokens_map
|
||||
)
|
||||
# get special system str
|
||||
round0_only_system_str = self.chat_template.render(
|
||||
messages=[{"role": "system", "content": ""}], add_generation_prompt=False, **self.special_tokens_map
|
||||
)
|
||||
# get special system and user str
|
||||
round0_system_user_str = self.chat_template.render(
|
||||
messages=[{"role": "system", "content": ""}] + conversation_dict[0][:1],
|
||||
add_generation_prompt=False,
|
||||
**self.special_tokens_map,
|
||||
)
|
||||
|
||||
# get user str = {special system and user str} - {special system str}
|
||||
user_str = replace_first_occurrence(round0_system_user_str, round0_only_system_str, "")
|
||||
# get system str = { system and user str} - {user str}
|
||||
system_str = round0_str.replace(user_str, "")
|
||||
|
||||
no_ans = []
|
||||
ans = []
|
||||
for conv in conversation_dict:
|
||||
roundi = [system] + conv if system else conv
|
||||
roundi_str = self.chat_template.render(
|
||||
messages=roundi, add_generation_prompt=False, **self.special_tokens_map
|
||||
)
|
||||
|
||||
roundi_no_ans = [system] + [conv[0]] if system else [conv[0]]
|
||||
roundi_no_ans_str = self.chat_template.render(
|
||||
messages=roundi_no_ans, add_generation_prompt=add_generation_prompt, **self.special_tokens_map
|
||||
)
|
||||
|
||||
roundi_ans_str = roundi_str[len(roundi_no_ans_str) :]
|
||||
ans.append(roundi_ans_str)
|
||||
|
||||
roundi_no_ans_no_system_str = replace_first_occurrence(roundi_no_ans_str, system_str, "")
|
||||
assert (
|
||||
roundi_no_ans_str == system_str + roundi_no_ans_no_system_str
|
||||
), f"the src string contains system str: {system_str}"
|
||||
no_ans.append(roundi_no_ans_no_system_str)
|
||||
|
||||
# the first round is special, we need to add system_str
|
||||
no_ans[0] = system_str + no_ans[0]
|
||||
|
||||
conversation_ids = []
|
||||
for i in range(len(no_ans)):
|
||||
conversation_ids.append(
|
||||
self.batch_encode(
|
||||
[no_ans[i], ans[i]],
|
||||
add_special_tokens=False,
|
||||
padding=False,
|
||||
)["input_ids"]
|
||||
)
|
||||
|
||||
result["conversations"] = conversation_ids
|
||||
return result
|
||||
@@ -0,0 +1,131 @@
|
||||
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
# Copyright 2024 The Qwen team, Alibaba Group and The HuggingFace Inc. team. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Tokenization classes for Qwen2."""
|
||||
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from ..tokenizer_utils import AddedToken
|
||||
from ..tokenizer_utils_fast import PretrainedTokenizerFast
|
||||
from .tokenizer import Qwen2Tokenizer
|
||||
|
||||
VOCAB_FILES_NAMES = {
|
||||
"vocab_file": "vocab.json",
|
||||
"merges_file": "merges.txt",
|
||||
"tokenizer_file": "tokenizer.json",
|
||||
}
|
||||
|
||||
|
||||
MAX_MODEL_INPUT_SIZES = {"qwen/qwen-tokenizer": 32768}
|
||||
|
||||
|
||||
class Qwen2TokenizerFast(PretrainedTokenizerFast):
|
||||
"""
|
||||
Construct a "fast" Qwen2 tokenizer (backed by PaddleNLP's *tokenizers* library). Based on byte-level
|
||||
Byte-Pair-Encoding.
|
||||
|
||||
Same with GPT2Tokenizer, this tokenizer has been trained to treat spaces like parts of the tokens so a word will
|
||||
be encoded differently whether it is at the beginning of the sentence (without space) or not:
|
||||
|
||||
```python
|
||||
>>> from transformers import Qwen2TokenizerFast
|
||||
|
||||
>>> tokenizer = Qwen2TokenizerFast.from_pretrained("Qwen/Qwen-tokenizer")
|
||||
>>> tokenizer("Hello world")["input_ids"]
|
||||
[9707, 1879]
|
||||
|
||||
>>> tokenizer(" Hello world")["input_ids"]
|
||||
[21927, 1879]
|
||||
```
|
||||
This is expected.
|
||||
|
||||
This tokenizer inherits from [`PreTrainedTokenizerFast`] which contains most of the main methods. Users should
|
||||
refer to this superclass for more information regarding those methods.
|
||||
|
||||
Args:
|
||||
vocab_file (`str`, *optional*):
|
||||
Path to the vocabulary file.
|
||||
merges_file (`str`, *optional*):
|
||||
Path to the merges file.
|
||||
tokenizer_file (`str`, *optional*):
|
||||
Path to [tokenizers](https://github.com/huggingface/tokenizers) file (generally has a .json extension) that
|
||||
contains everything needed to load the tokenizer.
|
||||
unk_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
|
||||
The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
|
||||
token instead. Not applicable to this tokenizer.
|
||||
bos_token (`str`, *optional*):
|
||||
The beginning of sequence token. Not applicable for this tokenizer.
|
||||
eos_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
|
||||
The end of sequence token.
|
||||
pad_token (`str`, *optional*, defaults to `"<|endoftext|>"`):
|
||||
The token used for padding, for example when batching sequences of different lengths.
|
||||
"""
|
||||
|
||||
vocab_files_names = VOCAB_FILES_NAMES
|
||||
resource_files_names = VOCAB_FILES_NAMES
|
||||
model_input_names = ["input_ids", "attention_mask"]
|
||||
slow_tokenizer_class = Qwen2Tokenizer
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
vocab_file=None,
|
||||
merges_file=None,
|
||||
tokenizer_file=None,
|
||||
unk_token="<|endoftext|>",
|
||||
bos_token=None,
|
||||
eos_token="<|endoftext|>",
|
||||
pad_token="<|endoftext|>",
|
||||
**kwargs,
|
||||
):
|
||||
# We need to at least pass vocab_file and merges_file to base class
|
||||
# in case a slow tokenizer needs to be initialized; other can be
|
||||
# configured through files.
|
||||
# following GPT2TokenizerFast, also adding unk_token, bos_token, and eos_token
|
||||
|
||||
bos_token = (
|
||||
AddedToken(bos_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(bos_token, str)
|
||||
else bos_token
|
||||
)
|
||||
eos_token = (
|
||||
AddedToken(eos_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(eos_token, str)
|
||||
else eos_token
|
||||
)
|
||||
unk_token = (
|
||||
AddedToken(unk_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(unk_token, str)
|
||||
else unk_token
|
||||
)
|
||||
pad_token = (
|
||||
AddedToken(pad_token, lstrip=False, rstrip=False, special=True, normalized=False)
|
||||
if isinstance(pad_token, str)
|
||||
else pad_token
|
||||
)
|
||||
|
||||
super().__init__(
|
||||
vocab_file=vocab_file,
|
||||
merges_file=merges_file,
|
||||
tokenizer_file=tokenizer_file,
|
||||
unk_token=unk_token,
|
||||
bos_token=bos_token,
|
||||
eos_token=eos_token,
|
||||
pad_token=pad_token,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Copied from transformers.models.gpt2.tokenization_gpt2_fast.GPT2TokenizerFast.save_vocabulary
|
||||
def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:
|
||||
files = self._tokenizer.model.save(save_directory, name=filename_prefix)
|
||||
return tuple(files)
|
||||
Reference in New Issue
Block a user