feat: 增加日志管理
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import logging
|
||||||
from .tokenizer import BpeTokenizer
|
from .tokenizer import BpeTokenizer
|
||||||
from .text import TextNormalizer
|
from .text import TextNormalizer
|
||||||
from .packing import SequencePacker
|
from .packing import SequencePacker
|
||||||
@@ -5,6 +6,10 @@ from .io import IOHandler
|
|||||||
from .processors import ProcessorFactory, BaseProcessor
|
from .processors import ProcessorFactory, BaseProcessor
|
||||||
from .export import export_dataset
|
from .export import export_dataset
|
||||||
from .cache import cache_jsonl
|
from .cache import cache_jsonl
|
||||||
|
from .utils import setup_logging
|
||||||
|
|
||||||
|
# 配置项目级日志记录
|
||||||
|
setup_logging()
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'BpeTokenizer',
|
'BpeTokenizer',
|
||||||
|
|||||||
+23
-11
@@ -1,6 +1,7 @@
|
|||||||
"""将 JSONL 文件 tokenize 后打包存储为 HDF5"""
|
"""Tokenize JSONL files and pack them into HDF5 storage."""
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
from typing import List
|
from typing import List
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -9,8 +10,12 @@ from tqdm import tqdm
|
|||||||
from .processors import BaseProcessor
|
from .processors import BaseProcessor
|
||||||
from .packing import SequencePacker
|
from .packing import SequencePacker
|
||||||
from .io import IOHandler
|
from .io import IOHandler
|
||||||
|
from .utils import error_handler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@error_handler()
|
||||||
def cache_jsonl(
|
def cache_jsonl(
|
||||||
files: List[str],
|
files: List[str],
|
||||||
output_dir: str,
|
output_dir: str,
|
||||||
@@ -20,17 +25,17 @@ def cache_jsonl(
|
|||||||
pad_value: int = 1,
|
pad_value: int = 1,
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
"""
|
"""
|
||||||
将 JSONL 文件 tokenize 后打包存储为 HDF5。
|
Tokenize JSONL files and pack them into HDF5 storage.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
files: JSONL 文件路径列表
|
files: List of JSONL file paths
|
||||||
output_dir: H5 输出目录
|
output_dir: H5 output directory
|
||||||
processor: 已初始化的 Processor 实例
|
processor: Initialized Processor instance
|
||||||
pack_size: 打包长度,<=0 表示不打包
|
pack_size: Packing length, <=0 means no packing
|
||||||
pad_value: 填充值
|
pad_value: Padding value
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
生成的 H5 文件路径列表
|
List of generated H5 file paths
|
||||||
"""
|
"""
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
output_files: List[str] = []
|
output_files: List[str] = []
|
||||||
@@ -40,8 +45,15 @@ def cache_jsonl(
|
|||||||
|
|
||||||
arrows = []
|
arrows = []
|
||||||
with open(file_path, "r", encoding="utf-8") as f:
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
for line in tqdm(f, desc=f"Processing {file_name}", leave=False):
|
for line_num, line in enumerate(tqdm(f, desc=f"Processing {file_name}", leave=False), start=1):
|
||||||
|
try:
|
||||||
arrow = processor.process(json.loads(line))
|
arrow = processor.process(json.loads(line))
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"JSON decode error in {file_path} line {line_num}: {e}. Skipping line.")
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Unexpected error processing line {line_num} in {file_path}: {e}. Skipping line.")
|
||||||
|
continue
|
||||||
if arrow is not None:
|
if arrow is not None:
|
||||||
arrows.append(arrow)
|
arrows.append(arrow)
|
||||||
|
|
||||||
@@ -50,7 +62,7 @@ def cache_jsonl(
|
|||||||
output = {}
|
output = {}
|
||||||
for key in processor.output_keys:
|
for key in processor.output_keys:
|
||||||
if pack_size > 0:
|
if pack_size > 0:
|
||||||
packer = SequencePacker(pack_size, pad_value) # 每个键独立实例
|
packer = SequencePacker(pack_size, pad_value) # independent instance per key
|
||||||
output[key] = packer.pack(package[key])
|
output[key] = packer.pack(package[key])
|
||||||
else:
|
else:
|
||||||
output[key] = package[key]
|
output[key] = package[key]
|
||||||
@@ -58,6 +70,6 @@ def cache_jsonl(
|
|||||||
IOHandler.save_h5(output_dir, file_name, output)
|
IOHandler.save_h5(output_dir, file_name, output)
|
||||||
h5_path = os.path.join(output_dir, f"{file_name}.h5")
|
h5_path = os.path.join(output_dir, f"{file_name}.h5")
|
||||||
output_files.append(h5_path)
|
output_files.append(h5_path)
|
||||||
print(f"Saved {h5_path}")
|
logger.info(f"Saved {h5_path}")
|
||||||
|
|
||||||
return output_files
|
return output_files
|
||||||
|
|||||||
+24
-15
@@ -1,33 +1,40 @@
|
|||||||
"""将 HuggingFace Dataset 分块导出为 JSONL 文件"""
|
"""Export HuggingFace Dataset to JSONL files in chunks."""
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Callable, Optional, List, Union
|
import logging
|
||||||
|
from typing import Callable, Optional, List, Union, Dict, Any
|
||||||
|
|
||||||
|
from datasets import Dataset
|
||||||
|
from .utils import error_handler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@error_handler()
|
||||||
def export_dataset(
|
def export_dataset(
|
||||||
dataset,
|
dataset: Dataset,
|
||||||
output_dir: str,
|
output_dir: str,
|
||||||
output_prefix: str,
|
output_prefix: str,
|
||||||
*,
|
*,
|
||||||
chunk_size: int = 1_000_000,
|
chunk_size: int = 1_000_000,
|
||||||
max_chunks: Optional[int] = None,
|
max_chunks: Optional[int] = None,
|
||||||
process_func: Optional[Callable] = None,
|
process_func: Optional[Callable[[Dict[str, Any]], Union[Dict[str, Any], List[Dict[str, Any]]]]] = None,
|
||||||
column: str = "text",
|
column: str = "text",
|
||||||
) -> List[str]:
|
) -> List[str]:
|
||||||
"""
|
"""
|
||||||
将 HuggingFace Dataset 分块导出为 JSONL 文件。
|
Export HuggingFace Dataset to JSONL files in chunks.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
dataset: HuggingFace Dataset 对象
|
dataset: HuggingFace Dataset object
|
||||||
output_dir: 输出目录
|
output_dir: Output directory
|
||||||
output_prefix: 输出文件名前缀,如 "chinese-c4-pretrain"
|
output_prefix: Output file name prefix, e.g., "chinese-c4-pretrain"
|
||||||
chunk_size: 每个文件的最大样本数
|
chunk_size: Maximum number of samples per file
|
||||||
max_chunks: 最多处理几个 chunk(用于调试)
|
max_chunks: Maximum number of chunks to process (for debugging)
|
||||||
process_func: 单条样本的转换函数 (dict) -> dict | list[dict]
|
process_func: Single sample transformation function (dict) -> dict | list[dict]
|
||||||
column: 默认提取的文本列名(仅在 process_func 为 None 时使用)
|
column: Default text column name (only used when process_func is None)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
生成的文件路径列表
|
List of generated file paths
|
||||||
"""
|
"""
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
total = len(dataset)
|
total = len(dataset)
|
||||||
@@ -41,14 +48,16 @@ def export_dataset(
|
|||||||
chunk = dataset.select(range(start, end))
|
chunk = dataset.select(range(start, end))
|
||||||
|
|
||||||
path = os.path.join(output_dir, f"{output_prefix}_chunk_{i}.jsonl")
|
path = os.path.join(output_dir, f"{output_prefix}_chunk_{i}.jsonl")
|
||||||
|
try:
|
||||||
with open(path, "w", encoding="utf-8") as f:
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
for example in chunk:
|
for example in chunk:
|
||||||
processed = process_func(example) if process_func else {column: example[column]}
|
processed = process_func(example) if process_func else {column: example[column]}
|
||||||
items = processed if isinstance(processed, list) else [processed]
|
items = processed if isinstance(processed, list) else [processed]
|
||||||
for item in items:
|
for item in items:
|
||||||
f.write(json.dumps(item, ensure_ascii=False) + "\n")
|
f.write(json.dumps(item, ensure_ascii=False) + "\n")
|
||||||
|
|
||||||
output_files.append(path)
|
output_files.append(path)
|
||||||
print(f"[{i + 1}/{lim}] Saved {path}")
|
logger.info(f"[{i + 1}/{lim}] Saved {path}")
|
||||||
|
except (OSError, IOError) as e:
|
||||||
|
logger.error(f"Failed to write chunk {i} to {path}: {e}")
|
||||||
|
|
||||||
return output_files
|
return output_files
|
||||||
|
|||||||
+7
-3
@@ -1,13 +1,15 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Optional, Callable
|
||||||
import os
|
import os
|
||||||
import h5py
|
import h5py
|
||||||
import torch
|
import torch
|
||||||
from torch import Tensor
|
from torch import Tensor
|
||||||
|
|
||||||
|
from .utils import error_handler
|
||||||
|
|
||||||
|
|
||||||
class IOHandler:
|
class IOHandler:
|
||||||
"""文件和 HDF5 读写"""
|
"""File and HDF5 read/write operations."""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fetch_files(directory: str) -> List[str]:
|
def fetch_files(directory: str) -> List[str]:
|
||||||
@@ -18,7 +20,7 @@ class IOHandler:
|
|||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fetch_folders(root_dir: str, filter_func=None) -> List[str]:
|
def fetch_folders(root_dir: str, filter_func: Optional[Callable[[str], bool]] = None) -> List[str]:
|
||||||
folders = []
|
folders = []
|
||||||
for root, dirs, _ in os.walk(root_dir):
|
for root, dirs, _ in os.walk(root_dir):
|
||||||
for dir_name in dirs:
|
for dir_name in dirs:
|
||||||
@@ -28,6 +30,7 @@ class IOHandler:
|
|||||||
return folders
|
return folders
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@error_handler()
|
||||||
def save_h5(output_dir: str, file_name: str, tensor_group: Dict[str, List[Tensor]]) -> None:
|
def save_h5(output_dir: str, file_name: str, tensor_group: Dict[str, List[Tensor]]) -> None:
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
full_path = os.path.join(output_dir, f"{file_name}.h5")
|
full_path = os.path.join(output_dir, f"{file_name}.h5")
|
||||||
@@ -38,6 +41,7 @@ class IOHandler:
|
|||||||
grp.create_dataset(f'data_{idx}', data=tensor.cpu().numpy())
|
grp.create_dataset(f'data_{idx}', data=tensor.cpu().numpy())
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@error_handler()
|
||||||
def load_h5(file_path: str, share_memory=True) -> Dict[str, List[Tensor]]:
|
def load_h5(file_path: str, share_memory=True) -> Dict[str, List[Tensor]]:
|
||||||
tensor_group: Dict[str, List[Tensor]] = {}
|
tensor_group: Dict[str, List[Tensor]] = {}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -3,12 +3,14 @@ from typing import List
|
|||||||
import torch
|
import torch
|
||||||
from torch import Tensor
|
from torch import Tensor
|
||||||
|
|
||||||
|
from .utils import error_handler
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SequencePacker:
|
class SequencePacker:
|
||||||
|
|
||||||
def __init__(self, pack_size: int, pad_value: int = 0, dtype=torch.int32):
|
def __init__(self, pack_size: int, pad_value: int = 0, dtype: torch.dtype = torch.int32):
|
||||||
self.pack_size = pack_size
|
self.pack_size = pack_size
|
||||||
self.pad_value = pad_value
|
self.pad_value = pad_value
|
||||||
self.dtype = dtype
|
self.dtype = dtype
|
||||||
@@ -21,6 +23,7 @@ class SequencePacker:
|
|||||||
)
|
)
|
||||||
self._current_pos = 0
|
self._current_pos = 0
|
||||||
|
|
||||||
|
@error_handler()
|
||||||
def pack(self, sequences: List[Tensor]) -> List[Tensor]:
|
def pack(self, sequences: List[Tensor]) -> List[Tensor]:
|
||||||
"""
|
"""
|
||||||
Pack sequences into fixed-size packages.
|
Pack sequences into fixed-size packages.
|
||||||
|
|||||||
+16
-14
@@ -1,13 +1,15 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Dict, List
|
from typing import Dict, List, Any
|
||||||
import torch
|
import torch
|
||||||
|
from torch import Tensor
|
||||||
|
from .tokenizer import BpeTokenizer
|
||||||
|
|
||||||
|
|
||||||
class BaseProcessor(ABC):
|
class BaseProcessor(ABC):
|
||||||
"""处理器抽象基类"""
|
"""Abstract base class for processors."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def process(self, input_dict: dict) -> dict:
|
def process(self, input_dict: Dict[str, Any]) -> Dict[str, Tensor]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -17,12 +19,12 @@ class BaseProcessor(ABC):
|
|||||||
|
|
||||||
|
|
||||||
class PreTrainProcessor(BaseProcessor):
|
class PreTrainProcessor(BaseProcessor):
|
||||||
"""预训练数据处理器"""
|
"""Pre-training data processor."""
|
||||||
|
|
||||||
def __init__(self, tokenizer):
|
def __init__(self, tokenizer: BpeTokenizer):
|
||||||
self.tokenizer = tokenizer
|
self.tokenizer = tokenizer
|
||||||
|
|
||||||
def process(self, input_dict: dict) -> dict:
|
def process(self, input_dict: Dict[str, Any]) -> Dict[str, Tensor]:
|
||||||
segment = input_dict["text"]
|
segment = input_dict["text"]
|
||||||
tokens = self.tokenizer.encode(f"{segment}<eos>")
|
tokens = self.tokenizer.encode(f"{segment}<eos>")
|
||||||
return {'sequence': torch.tensor(tokens, dtype=torch.int32)}
|
return {'sequence': torch.tensor(tokens, dtype=torch.int32)}
|
||||||
@@ -33,12 +35,12 @@ class PreTrainProcessor(BaseProcessor):
|
|||||||
|
|
||||||
|
|
||||||
class SFTProcessor(BaseProcessor):
|
class SFTProcessor(BaseProcessor):
|
||||||
"""监督微调数据处理器"""
|
"""Supervised fine-tuning data processor."""
|
||||||
|
|
||||||
def __init__(self, tokenizer):
|
def __init__(self, tokenizer: BpeTokenizer):
|
||||||
self.tokenizer = tokenizer
|
self.tokenizer = tokenizer
|
||||||
|
|
||||||
def process(self, input_dict: dict) -> dict:
|
def process(self, input_dict: Dict[str, Any]) -> Dict[str, Tensor]:
|
||||||
query, response = input_dict["query"], input_dict["response"]
|
query, response = input_dict["query"], input_dict["response"]
|
||||||
q = self.tokenizer.encode(
|
q = self.tokenizer.encode(
|
||||||
f"<|im_start|>user\n{query}<|im_end|>\n<|im_start|>assistant\n"
|
f"<|im_start|>user\n{query}<|im_end|>\n<|im_start|>assistant\n"
|
||||||
@@ -55,12 +57,12 @@ class SFTProcessor(BaseProcessor):
|
|||||||
|
|
||||||
|
|
||||||
class DPOProcessor(BaseProcessor):
|
class DPOProcessor(BaseProcessor):
|
||||||
"""DPO 偏好学习数据处理器"""
|
"""DPO preference learning data processor."""
|
||||||
|
|
||||||
def __init__(self, tokenizer):
|
def __init__(self, tokenizer: BpeTokenizer):
|
||||||
self.tokenizer = tokenizer
|
self.tokenizer = tokenizer
|
||||||
|
|
||||||
def process(self, input_dict: dict) -> dict:
|
def process(self, input_dict: Dict[str, Any]) -> Dict[str, Tensor]:
|
||||||
query = input_dict["query"]
|
query = input_dict["query"]
|
||||||
chosen_response = input_dict["chosen"]
|
chosen_response = input_dict["chosen"]
|
||||||
rejected_response = input_dict["rejected"]
|
rejected_response = input_dict["rejected"]
|
||||||
@@ -92,7 +94,7 @@ class DPOProcessor(BaseProcessor):
|
|||||||
|
|
||||||
|
|
||||||
class ProcessorFactory:
|
class ProcessorFactory:
|
||||||
"""处理器工厂"""
|
"""Processor factory."""
|
||||||
|
|
||||||
_processors = {
|
_processors = {
|
||||||
"pt": PreTrainProcessor,
|
"pt": PreTrainProcessor,
|
||||||
@@ -101,7 +103,7 @@ class ProcessorFactory:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, processor_type: str, tokenizer) -> BaseProcessor:
|
def create(cls, processor_type: str, tokenizer: BpeTokenizer) -> BaseProcessor:
|
||||||
if processor_type not in cls._processors:
|
if processor_type not in cls._processors:
|
||||||
raise ValueError(f"Invalid processor type: {processor_type}")
|
raise ValueError(f"Invalid processor type: {processor_type}")
|
||||||
return cls._processors[processor_type](tokenizer)
|
return cls._processors[processor_type](tokenizer)
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
import re
|
import re
|
||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
|
||||||
class TextNormalizer:
|
class TextNormalizer:
|
||||||
"""文本规范化"""
|
"""Text normalization."""
|
||||||
|
|
||||||
DEFAULT_REPLACEMENTS = {
|
DEFAULT_REPLACEMENTS = {
|
||||||
"\\[": "$$", "\\]": "$$", "\\(": "$", "\\)": "$",
|
"\\[": "$$", "\\]": "$$", "\\(": "$", "\\)": "$",
|
||||||
@@ -13,7 +13,7 @@ class TextNormalizer:
|
|||||||
'\u00A0': ' ', '\u2026': '...'
|
'\u00A0': ' ', '\u2026': '...'
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, custom_rules: Dict[str, str] = None):
|
def __init__(self, custom_rules: Optional[Dict[str, str]] = None):
|
||||||
self.replacements = {**self.DEFAULT_REPLACEMENTS, **(custom_rules or {})}
|
self.replacements = {**self.DEFAULT_REPLACEMENTS, **(custom_rules or {})}
|
||||||
self._pattern = re.compile('|'.join(re.escape(k) for k in self.replacements))
|
self._pattern = re.compile('|'.join(re.escape(k) for k in self.replacements))
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ from tokenizers import Tokenizer, Encoding
|
|||||||
from tokenizers import decoders, processors, normalizers, pre_tokenizers
|
from tokenizers import decoders, processors, normalizers, pre_tokenizers
|
||||||
from tokenizers.models import BPE
|
from tokenizers.models import BPE
|
||||||
from tokenizers.trainers import BpeTrainer
|
from tokenizers.trainers import BpeTrainer
|
||||||
from typing import List, Union
|
from typing import List, Union, Optional, Tuple, Iterator
|
||||||
|
|
||||||
|
|
||||||
class BpeTokenizer:
|
class BpeTokenizer:
|
||||||
def __init__(self, path=None):
|
def __init__(self, path: Optional[str] = None):
|
||||||
self._control_tokens = ["<bos>", "<eos>", "<pad>"]
|
self._control_tokens = ["<bos>", "<eos>", "<pad>"]
|
||||||
self._special_tokens = ["<|im_start|>", "<|im_end|>"]
|
self._special_tokens = ["<|im_start|>", "<|im_end|>"]
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ class BpeTokenizer:
|
|||||||
if path is not None:
|
if path is not None:
|
||||||
self._tokenizer = Tokenizer.from_file(path)
|
self._tokenizer = Tokenizer.from_file(path)
|
||||||
|
|
||||||
def _prepare_trainer(self, vocab_size: int, min_freq: int, reserved_token_size: int, max_token_length=18) -> tuple:
|
def _prepare_trainer(self, vocab_size: int, min_freq: int, reserved_token_size: int, max_token_length: int = 18) -> Tuple[BpeTrainer, int, List[str]]:
|
||||||
assert reserved_token_size > len(self._special_tokens)
|
assert reserved_token_size > len(self._special_tokens)
|
||||||
reserved_tokens = [f"<|reserve{i:02d}|>" for i in range(reserved_token_size - len(self._special_tokens))]
|
reserved_tokens = [f"<|reserve{i:02d}|>" for i in range(reserved_token_size - len(self._special_tokens))]
|
||||||
detail_vocab_size = vocab_size - (len(reserved_tokens) + len(self._special_tokens))
|
detail_vocab_size = vocab_size - (len(reserved_tokens) + len(self._special_tokens))
|
||||||
@@ -49,7 +49,7 @@ class BpeTokenizer:
|
|||||||
|
|
||||||
return trainer, detail_vocab_size, reserved_tokens
|
return trainer, detail_vocab_size, reserved_tokens
|
||||||
|
|
||||||
def train(self, files, vocab_size, min_freq, reserved_token_size=100):
|
def train(self, files: List[str], vocab_size: int, min_freq: int, reserved_token_size: int = 100) -> None:
|
||||||
trainer, _, reserved_tokens = self._prepare_trainer(
|
trainer, _, reserved_tokens = self._prepare_trainer(
|
||||||
vocab_size=vocab_size,
|
vocab_size=vocab_size,
|
||||||
min_freq=min_freq,
|
min_freq=min_freq,
|
||||||
@@ -58,7 +58,7 @@ class BpeTokenizer:
|
|||||||
self._tokenizer.train(files=files, trainer=trainer)
|
self._tokenizer.train(files=files, trainer=trainer)
|
||||||
self._tokenizer.add_special_tokens(self._special_tokens + reserved_tokens)
|
self._tokenizer.add_special_tokens(self._special_tokens + reserved_tokens)
|
||||||
|
|
||||||
def train_from_iterator(self, iterator, vocab_size, min_freq, reserved_token_size=100):
|
def train_from_iterator(self, iterator: Iterator[str], vocab_size: int, min_freq: int, reserved_token_size: int = 100) -> None:
|
||||||
trainer, _, reserved_tokens = self._prepare_trainer(
|
trainer, _, reserved_tokens = self._prepare_trainer(
|
||||||
vocab_size=vocab_size,
|
vocab_size=vocab_size,
|
||||||
min_freq=min_freq,
|
min_freq=min_freq,
|
||||||
@@ -67,13 +67,13 @@ class BpeTokenizer:
|
|||||||
self._tokenizer.train_from_iterator(iterator=iterator, trainer=trainer)
|
self._tokenizer.train_from_iterator(iterator=iterator, trainer=trainer)
|
||||||
self._tokenizer.add_special_tokens(self._special_tokens + reserved_tokens)
|
self._tokenizer.add_special_tokens(self._special_tokens + reserved_tokens)
|
||||||
|
|
||||||
def save(self, path):
|
def save(self, path: str) -> None:
|
||||||
self._tokenizer.save(path)
|
self._tokenizer.save(path)
|
||||||
|
|
||||||
def load(self, path):
|
def load(self, path: str) -> None:
|
||||||
self._tokenizer = Tokenizer.from_file(path)
|
self._tokenizer = Tokenizer.from_file(path)
|
||||||
|
|
||||||
def encode(self, tokens: Union[str, List[str]], out_ids: bool=True, add_special_tokens: bool=False) -> List:
|
def encode(self, tokens: Union[str, List[str]], out_ids: bool = True, add_special_tokens: bool = False) -> Union[List[int], List[str], List[List[int]], List[List[str]]]:
|
||||||
if isinstance(tokens, str):
|
if isinstance(tokens, str):
|
||||||
encoded: Encoding = self._tokenizer.encode(tokens, add_special_tokens=add_special_tokens)
|
encoded: Encoding = self._tokenizer.encode(tokens, add_special_tokens=add_special_tokens)
|
||||||
return encoded.ids if out_ids else encoded.tokens
|
return encoded.ids if out_ids else encoded.tokens
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import functools
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from typing import Optional, Callable, Any
|
||||||
|
|
||||||
|
|
||||||
|
def error_handler(
|
||||||
|
logger: Optional[logging.Logger] = None,
|
||||||
|
reraise: bool = True,
|
||||||
|
log_level: int = logging.ERROR,
|
||||||
|
capture_keyboard_interrupt: bool = False,
|
||||||
|
):
|
||||||
|
def decorator(func: Callable) -> Callable:
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs) -> Any:
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
if not capture_keyboard_interrupt and isinstance(e, KeyboardInterrupt):
|
||||||
|
raise
|
||||||
|
|
||||||
|
nonlocal logger
|
||||||
|
log = logger or logging.getLogger(func.__module__)
|
||||||
|
log.log(
|
||||||
|
log_level,
|
||||||
|
f"Error in {func.__name__}: {e}",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
if reraise:
|
||||||
|
raise
|
||||||
|
return None
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(level: Optional[int] = None) -> None:
|
||||||
|
|
||||||
|
if level is None:
|
||||||
|
level_name = os.getenv("LOG_LEVEL", "INFO").upper()
|
||||||
|
level = getattr(logging, level_name, logging.INFO)
|
||||||
|
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
if root_logger.handlers:
|
||||||
|
root_logger.setLevel(level)
|
||||||
|
return
|
||||||
|
|
||||||
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
console_handler.setLevel(level)
|
||||||
|
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S",
|
||||||
|
)
|
||||||
|
console_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
root_logger.setLevel(level)
|
||||||
|
root_logger.addHandler(console_handler)
|
||||||
|
|
||||||
|
logging.getLogger("h5py").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("torch").setLevel(logging.WARNING)
|
||||||
Reference in New Issue
Block a user