使用前馈神经网络和卷积神经网络实现姓氏分类

一、环境配置

python 3.7

torch==2.1.0

二、数据预处理

姓氏数据集,它收集了来自18个不同国家的10,000个姓氏,这些姓氏是作者从互联网上不同的姓名来源收集的。该数据集具有一些使其有趣的性质。第一个性质是它是相当不平衡的。排名前三的国籍占数据的60%以上:27%是英语,21%是俄语,14%是阿拉伯语。剩下的15个民族的频率也在下降——这也是语言特有的特性。第二个特点是,在国籍和姓氏正字法(拼写)之间有一种有效和直观的关系。有些拼写变体与原籍国联系非常紧密(比如“O ‘Neill”、“Antonopoulos”、“Nagasawa”或“Zhu”)。本文中提到的姓氏分类是指将姓氏分类至其原有国籍类别中。

2.1 关于MLP的数据预处理

class SurnameDataset(Dataset):
    def __init__(self, surname_df, vectorizer):
        """
        参数:
            surname_df (pandas.DataFrame): 数据集
            vectorizer (SurnameVectorizer): 从数据集实例化的向量化器
        """
        self.surname_df = surname_df
        self._vectorizer = vectorizer
 
        self.train_df = self.surname_df[self.surname_df.split=='train']  # 训练集
        self.train_size = len(self.train_df)  # 训练集大小
 
        self.val_df = self.surname_df[self.surname_df.split=='val']  # 验证集
        self.validation_size = len(self.val_df)  # 验证集大小
 
        self.test_df = self.surname_df[self.surname_df.split=='test']  # 测试集
        self.test_size = len(self.test_df)  # 测试集大小
 
        self._lookup_dict = {
            'train': (self.train_df, self.train_size),  # 查找字典

        self.set_split('train')  # 默认设置为训练集
        
        # 类权重
        class_counts = surname_df.nationality.value_counts().to_dict()  # 统计每个类别的数量
        def sort_key(item):
            return self._vectorizer.nationality_vocab.lookup_token(item[0])
        sorted_counts = sorted(class_counts.items(), key=sort_key)  # 根据类别排序
        frequencies = [count for _, count in sorted_counts]
        self.class_weights = 1.0 / torch.tensor(frequencies, dtype=torch.float32)  # 计算类权重
 
    @classmethod
    def load_dataset_and_make_vectorizer(cls, surname_csv):
        """加载数据集并从头开始创建一个新的向量化器
        
        参数:
            surname_csv (str): 数据集的位置
        返回:
            SurnameDataset 的一个实例
        """
        surname_df = pd.read_csv(surname_csv)  # 读取CSV文件
        train_surname_df = surname_df[surname_df.split=='train']  # 提取训练集数据
        return cls(surname_df, SurnameVectorizer.from_dataframe(train_surname_df))  # 创建并返回实例
 
    @classmethod
    def load_dataset_and_load_vectorizer(cls, surname_csv, vectorizer_filepath):
        """加载数据集和相应的向量化器。
        在向量化器已被缓存以便重用的情况下使用
        
        参数:
            surname_csv (str): 数据集的位置
            vectorizer_filepath (str): 保存向量化器的位置
        返回:
            SurnameDataset 的一个实例
        """
        surname_df = pd.read_csv(surname_csv)  # 读取CSV文件
        vectorizer = cls.load_vectorizer_only(vectorizer_filepath)  # 加载向量化器
        return cls(surname_df, vectorizer)  # 创建并返回实例
 
    @staticmethod
    def load_vectorizer_only(vectorizer_filepath):
        """一个静态方法,从文件中加载向量化器
        
        参数:
            vectorizer_filepath (str): 序列化向量化器的位置
        返回:
            SurnameVectorizer 的一个实例
        """
        with open(vectorizer_filepath) as fp:
            return SurnameVectorizer.from_serializable(json.load(fp))  # 反序列化并返回向量化器
 
    def save_vectorizer(self, vectorizer_filepath):
        """使用json将向量化器保存到磁盘
        
        参数:
            vectorizer_filepath (str): 保存向量化器的位置
        """
        with open(vectorizer_filepath, "w") as fp:
            json.dump(self._vectorizer.to_serializable(), fp)  # 序列化并保存向量化器
 
    def get_vectorizer(self):
        """ 返回向量化器 """
        return self._vectorizer
 
    def set_split(self, split="train"):
        """ 使用数据框中的列选择数据集的分割 """
        self._target_split = split
        self._target_df, self._target_size = self._lookup_dict[split]  # 设置目标数据集及其大小
 
    def __len__(self):
        return self._target_size  # 返回当前数据集的大小
 
    def __getitem__(self, index):
        """PyTorch数据集的主要入口方法
        
        参数:
            index (int): 数据点的索引
        返回:
            包含数据点的字典:
                特征 (x_surname)
                标签 (y_nationality)
        """
        row = self._target_df.iloc[index]  # 获取指定索引处的数据行
 
        surname_vector = self._vectorizer.vectorize(row.surname)  # 向量化姓氏
 
        nationality_index = self._vectorizer.nationality_vocab.lookup_token(row.nationality)  # 获取国籍的索引
 
        return {'x_surname': surname_vector, 'y_nationality': nationality_index}  # 返回包含特征和标签的字典
 
    def get_num_batches(self, batch_size):
        """给定批次大小,返回数据集中的批次数量
        
        参数:
            batch_size (int)
        返回:
            数据集中的批次数量
        """
        return len(self) // batch_size  # 计算批次数量
 
    
def generate_batches(dataset, batch_size, shuffle=True, drop_last=True, device="cpu"): 
    """
    一个生成器函数,包装了PyTorch的DataLoader。
    它将确保每个张量在正确的设备位置上。
    """
    dataloader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last)  # 创建DataLoader
 
    for data_dict in dataloader:
        out_data_dict = {}
        for name, tensor in data_dict.items():
            out_data_dict[name] = data_dict[name].to(device)  # 将张量移动到指定设备
        yield out_data_dict  # 生成批次数据

我们定义了一个姓氏数据集类和一个批次生成器函数,用于处理和加载姓氏数据,以便在机器学习模型中使用。

class Vocabulary(object):
    """处理文本并提取用于映射的词汇的类"""
 
    def __init__(self, token_to_idx=None, add_unk=True, unk_token="<UNK>"):
        """
        参数:
            token_to_idx (dict): 预先存在的词汇到索引的映射
            add_unk (bool): 是否添加UNK(未知)标记的标志
            unk_token (str): 要添加到词汇中的UNK标记
        """
 
        if token_to_idx is None:
            token_to_idx = {}
        self._token_to_idx = token_to_idx
 
        self._idx_to_token = {idx: token 
                              for token, idx in self._token_to_idx.items()}
        
        self._add_unk = add_unk
        self._unk_token = unk_token
        
        self.unk_index = -1
        if add_unk:
            self.unk_index = self.add_token(unk_token)  # 添加UNK标记并获取其索引
        
    def to_serializable(self):
        """ 返回可以序列化的字典 """
        return {'token_to_idx': self._token_to_idx, 
                'add_unk': self._add_unk, 
                'unk_token': self._unk_token}
 
    @classmethod
    def from_serializable(cls, contents):
        """ 从序列化字典实例化词汇表 """
        return cls(**contents)
 
    def add_token(self, token):
        """根据标记更新映射字典。
        
        参数:
            token (str): 要添加到词汇表中的项
        返回:
            index (int): 对应于该标记的整数索引
        """
        try:
            index = self._token_to_idx[token]
        except KeyError:
            index = len(self._token_to_idx)
            self._token_to_idx[token] = index
            self._idx_to_token[index] = token
        return index
    
    def add_many(self, tokens):
        """将标记列表添加到词汇表中
        
        参数:
            tokens (list): 字符串标记的列表
        返回:
            indices (list): 对应于这些标记的索引列表
        """
        return [self.add_token(token) for token in tokens]
 
    def lookup_token(self, token):
        """检索与标记关联的索引,如果标记不存在则返回UNK索引。
        
        参数:
            token (str): 要查找的标记
        返回:
            index (int): 对应于该标记的索引
        注意:
            `unk_index`需要 >=0(已添加到词汇表中)以实现UNK功能 
        """
        if self.unk_index >= 0:
            return self._token_to_idx.get(token, self.unk_index)
        else:
            return self._token_to_idx[token]
 
    def lookup_index(self, index):
        """返回与索引关联的标记
        
        参数: 
            index
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值