RNN人名分类器案例

RNN人名分类器案例

1 任务目的:

目的: 给定一个人名,来判定这个人名属于哪个国家
典型的文本分类任务: 18分类---多分类任务

2 数据格式

  • 注意:两列数据,第一列是人名,第二列是国家类别,中间用制表符号"\t"隔开

Ang Chinese
AuYong  Chinese
Yuasa   Japanese
Yuhara  Japanese
Yunokawa    Japanese

3 任务实现流程

1. 获取数据:案例中是直接给定的
2. 数据预处理: 脏数据清洗、数据格式转换、数据源Dataset的构造、数据迭代器Dataloader的构造
3. 模型搭建: RNN、LSTM、GRU一系列模型
4. 模型训练和评估(测试)
5. 模型上线---API接口(后续会讲)

4 数据预处理

4.1读取txt文档数据

目的:

将文档里面的数据读取到内存中,实际上我们做了一个操作: 将人名存放到一个列表中,国家类别存放到一个列表中

代码实现

def read_data(filename):# 1. 初始化两个空列表my_list_x, my_list_y = [], []# 2. 读取文件内容with open(filename,'r', encoding='utf-8') as fr:for line in fr.readlines():if len(line) <= 5:continue# strip()方法默认将字符串首尾两端的空白去掉x, y = line.strip().split('\t')my_list_x.append(x)my_list_y.append(y)
​return my_list_x, my_list_y

4.2 构建自己的数据源DataSet

目的:

使用Pytorch框架,一般遵从一个规矩:使用DataSet方法构造数据源,来让模型进行使用
构造数据源的过程中:必须继承torch.utils.data.Dataset类,必须构造两个魔法方法:__len__(), __getitem__()
__len__(): 一般返回的是样本的总个数,我们可以直接len(dataset对象)直接就可以获得结果
__getitem__(): 可以根据某个索引取出样本值,我们可以直接用dataset对象[index]来直接获得结果

代码实现:

class NameClassDataset(Dataset):def __init__(self, mylist_x, mylist_y):self.mylist_x = mylist_xself.mylist_y = mylist_yself.sample_len = len(mylist_x)
​# 定义魔法方法lendef __len__(self):return self.sample_len
​# 定义魔法方法getitemdef __getitem__(self, index):# 1.index异常值处理index = min(max(index, 0), self.sample_len - 1)# 2. 根据index取出人名和国家名x = self.mylist_x[index]# print(f'x--->{x}')y = self.mylist_y[index]# print(f'y--->{y}')# 3.需要对人名进行one-hot编码表示:这里的思路是:针对每个人名组成的单词进行one-hot,然后再拼接tensor_x = torch.zeros(len(x), n_letter)# print(f'tensor_x-->{tensor_x}')for li, letter in enumerate(x):tensor_x[li][all_letters.find(letter)] = 1# 4.获取标签#  print(f'dataset内部的tensor_x--》{tensor_x.shape}')tensor_y = torch.tensor(categorys.index(y), dtype=torch.long)# print(f'dataset内部的tensor_y-->{tensor_y}')return tensor_x, tensor_y

4.3 构建数据迭代器Dataloader

目的:

为了将Dataset我们上一步构建的数据源,进行再次封装,变成一个迭代器,可以进行for循环,而且,可以自动为我们dataset里面的数据进行增维(bath_size),也可以随机打乱我们的取值顺序

代码实现:

filename = './data/name_classfication.txt'
my_list_x, my_list_y = read_data(filename)
mydataset = NameClassDataset(mylist_x=my_list_x, mylist_y=my_list_y)
my_dataloader = DataLoader(dataset=mydataset, batch_size=1, shuffle=True)

5 模型搭建

5.1 搭建RNN模型

  • 注意事项

RNN模型在实例化的时候,默认batch_first=False,因此,需要小心输入数据的形状
因为: dataloader返回的结果x---》shape--〉[batch_size, seq_len, input_size], 所以课堂上代码和讲义稍微有点不同,讲义是默认的batch_first=False,而我们的代码是batch_first=True,这样做的目的,可以直接承接x的输入。

  • 代码实现

class MyRNN(nn.Module):def __init__(self, input_size, hidden_size, ouput_size, num_layers=1):super().__init__()# input_size 代表词嵌入维度;self.input_size = input_size# hidden_size代表RNN隐藏层维度self.hidden_size = hidden_size# output_size代表:国家种类个数self.ouput_size = ouput_sizeself.num_layers = num_layers# 定义RNN网络层# 和讲义不一样,我设定了batch_first=True,意味着rnn接受的input第一个参数是batch_sizeself.rnn = nn.RNN(self.input_size, self.hidden_size,num_layers=self.num_layers, batch_first=True)# 定义输出网络层self.linear = nn.Linear(self.hidden_size, self.ouput_size)
​# 定义softmax层self.softmax = nn.LogSoftmax(dim=-1)
​def forward(self, input, hidden):# input的shape---》[batch_size, seq_len, input_size] [1, 9, 57]# hidden的shape---》[num_layers, batch_size, hidden_size] [1,1,128]
​# 将input和hidden送入RNN模型得到结果rnn_output【1,9,128】,rnn_hn[1,1,128]rnn_output, rnn_hn = self.rnn(input, hidden)# print(f'rnn_output--》{rnn_output.shape}')# temp:[1, 128]tmep = rnn_output[0][-1].unsqueeze(0)# print(f'tmep--》{tmep.shape}')# 将临时tmep:代表当前样本最后一词的隐藏层输出结果[1, 18]output = self.linear(tmep)# print(f'output--》{output.shape}')# 经过softmaxreturn self.softmax(output), rnn_hn
​def inithidden(self):return torch.zeros(self.num_layers, 1, self.hidden_size)

RNN模型测试

def test_RNN():# 1.得到数据my_dataloader = get_dataloader()# 2.实例化模型input_size = n_letter # 57hidden_size = 128 # 自定设定RNN模型输出结果维度output_size = len(categorys) # 18my_rnn = MyRNN(input_size, hidden_size, output_size)h0 = my_rnn.inithidden()# 3.将数据送入模型for i, (x, y) in enumerate(my_dataloader):print(f'x--->{x.shape}')output, hn = my_rnn(input=x, hidden=h0)print(f'output模型输出结果-->{output.shape}')print(f'hn-->{hn.shape}')break

5.2 搭建LSTM模型

  • 注意事项

LSTM模型在实例化的时候,默认batch_first=False,因此,需要小心输入数据的形状 因为: dataloader返回的结果x---》shape--〉[batch_size, seq_len, input_size], 所以课堂上代码和讲义稍微有点不同,讲义是默认的batch_first=False,而我们的代码是batch_first=True,这样做的目的,可以直接承接x的输入。

  • 代码实现

class MyLSTM(nn.Module):def __init__(self, input_size, hidden_size, ouput_size, num_layers=1):super().__init__()# input_size 代表词嵌入维度;self.input_size = input_size# hidden_size代表RNN隐藏层维度self.hidden_size = hidden_size# output_size代表:国家种类个数self.ouput_size = ouput_sizeself.num_layers = num_layers# 定义LSTM网络层# 和讲义不一样,我设定了batch_first=True,意味着rnn接受的input第一个参数是batch_sizeself.lstm = nn.LSTM(self.input_size, self.hidden_size,num_layers=self.num_layers, batch_first=True)# 定义输出网络层self.linear = nn.Linear(self.hidden_size, self.ouput_size)
​# 定义softmax层self.softmax = nn.LogSoftmax(dim=-1)
​def forward(self, input, hidden, c0):# input的shape---》[batch_size, seq_len, input_size] [1, 9, 57]# hidden的shape---》[num_layers, batch_size, hidden_size] [1,1,128]
​# 将input和hidden送入RNN模型得到结果rnn_output【1,9,128】,rnn_hn[1,1,128]lstm_output, (lstm_hn, lstm_cn) = self.lstm(input, (hidden, c0))# print(f'rnn_output--》{rnn_output.shape}')# temp:[1, 128]tmep = lstm_output[0][-1].unsqueeze(0)# print(f'tmep--》{tmep.shape}')# 将临时tmep:代表当前样本最后一词的隐藏层输出结果[1, 18]output = self.linear(tmep)# print(f'output--》{output.shape}')# 经过softmaxreturn self.softmax(output), lstm_hn, lstm_cn
​def inithidden(self):h0 = torch.zeros(self.num_layers, 1, self.hidden_size)c0 = torch.zeros(self.num_layers, 1, self.hidden_size)return h0, c0

LSTM测试

def test_LSTM():# 1.得到数据my_dataloader = get_dataloader()# 2.实例化模型input_size = n_letter # 57hidden_size = 128 # 自定设定LSTM模型输出结果维度output_size = len(categorys) # 18my_lstm = MyLSTM(input_size, hidden_size, output_size)h0, c0 = my_lstm.inithidden()# 3.将数据送入模型for i, (x, y) in enumerate(my_dataloader):print(f'x--->{x.shape}')output, hn, cn = my_lstm(input=x, hidden=h0, c0=c0)print(f'output模型输出结果-->{output.shape}')print(f'hn-->{hn.shape}')print(f'cn-->{cn.shape}')break

5.3 搭建GRU模型

  • 注意事项

GRU模型在实例化的时候,默认batch_first=False,因此,需要小心输入数据的形状 因为: dataloader返回的结果x---》shape--〉[batch_size, seq_len, input_size], 所以课堂上代码和讲义稍微有点不同,讲义是默认的batch_first=False,而我们的代码是batch_first=True,这样做的目的,可以直接承接x的输入。

  • 代码实现

class MyGRU(nn.Module):def __init__(self, input_size, hidden_size, ouput_size, num_layers=1):super().__init__()# input_size 代表词嵌入维度;self.input_size = input_size# hidden_size代表RNN隐藏层维度self.hidden_size = hidden_size# output_size代表:国家种类个数self.ouput_size = ouput_sizeself.num_layers = num_layers# 定义GRU网络层# 和讲义不一样,我设定了batch_first=True,意味着rnn接受的input第一个参数是batch_sizeself.gru = nn.GRU(self.input_size, self.hidden_size,num_layers=self.num_layers, batch_first=True)# 定义输出网络层self.linear = nn.Linear(self.hidden_size, self.ouput_size)
​# 定义softmax层self.softmax = nn.LogSoftmax(dim=-1)
​def forward(self, input, hidden):# input的shape---》[batch_size, seq_len, input_size] [1, 9, 57]# hidden的shape---》[num_layers, batch_size, hidden_size] [1,1,128]
​# 将input和hidden送入RNN模型得到结果rnn_output【1,9,128】,rnn_hn[1,1,128]gru_output, gru_hn = self.gru(input, hidden)# print(f'rnn_output--》{rnn_output.shape}')# temp:[1, 128]tmep = gru_output[0][-1].unsqueeze(0)# print(f'tmep--》{tmep.shape}')# 将临时tmep:代表当前样本最后一词的隐藏层输出结果[1, 18]output = self.linear(tmep)# print(f'output--》{output.shape}')# 经过softmaxreturn self.softmax(output), gru_hn
​def inithidden(self):return torch.zeros(self.num_layers, 1, self.hidden_size)

GRU测试

def test_GRU():# 1.得到数据my_dataloader = get_dataloader()# 2.实例化模型input_size = n_letter # 57hidden_size = 128 # 自定设定RNN模型输出结果维度output_size = len(categorys) # 18my_gru = MyGRU(input_size, hidden_size, output_size)# 2.1 初始化参数h0 = my_gru.inithidden()# 3.将数据送入模型for i, (x, y) in enumerate(my_dataloader):print(f'x--->{x.shape}')output, hn = my_gru(input=x, hidden=h0)print(f'output模型输出结果-->{output.shape}')print(f'hn-->{hn.shape}')break

6 模型训练

基本过程

1.获取数据
2.构建数据源Dataset
3.构建数据迭代器Dataloader
4.加载自定义的模型
5.实例化损失函数对象
6.实例化优化器对象
7.定义打印日志参数
8.开始训练
8.1 实现外层大循环epoch
(可以在这构建数据迭代器Dataloader)
8.2 内部遍历数据迭代器dataloader
8.3 将数据送入模型得到输出结果
8.4 计算损失
8.5 梯度清零: optimizer.zero_grad()
8.6 反向传播: loss.backward()
8.7 参数更新(梯度更新): optimizer.step()
8.8 打印训练日志
9. 保存模型: torch.save(model.state_dict(), "model_path")

6.1 RNN模型训练代码实现

my_lr = 1e-3
epochs = 1
# 训练rnn模型
def train_rnn():# 读取数据my_list_x, my_list_y = read_data(filepath='./data/name_classfication.txt')# 实例化dataset数据源对象my_dataset = NameClassDataset(my_list_x, my_list_y)# 实例化模型# n_letters=57, hidden_size=128,类别总数output_size=18my_rnn = My_RNN(input_size=57, hidden_size=128, output_size=18)# 实例化损失函数对象my_nll_loss = nn.NLLLoss()# 实例化优化器对象my_optim = optim.Adam(my_rnn.parameters(), lr=my_lr)# 定义打印日志的参数start_time = time.time()total_iter_num = 0 # 当前已经训练的样本总数total_loss = 0  # 已经训练的损失值total_loss_list = [] # 每隔n个样本,保存平均损失值total_acc_num = 0 # 预测正确的样本个数total_acc_list = [] # 每隔n个样本,保存平均准确率# 开始训练for epoch_idx in range(epochs):# 实例化dataloadermy_dataloader = DataLoader(dataset=my_dataset, batch_size=1, shuffle=True)# 开始内部迭代数据,送入模型for i, (x, y) in enumerate(tqdm(my_dataloader)):# print(f'x--》{x.shape}')# print(f'y--》{y}')output, hn = my_rnn(input=x[0], hidden=my_rnn.inithidden())# print(f'output--》{output}') # [1, 18]# 计算损失my_loss = my_nll_loss(output, y)# print(f'my_loss--》{my_loss}')# print(f'my_loss--》{type(my_loss)}')
​# 梯度清零my_optim.zero_grad()# 反向传播my_loss.backward()# 梯度更新my_optim.step()
​# 统计一下已经训练样本的总个数total_iter_num = total_iter_num + 1
​# 统计一下已经训练样本的总损失total_loss = total_loss + my_loss.item()
​# 统计已经训练的样本中预测正确的个数i_predict_num = 1 if torch.argmax(output).item() == y.item() else 0total_acc_num = total_acc_num + i_predict_num# 每隔100次训练保存一下平均损失和准确率if total_iter_num % 100 == 0:avg_loss = total_loss / total_iter_numtotal_loss_list.append(avg_loss)
​avg_acc = total_acc_num / total_iter_numtotal_acc_list.append(avg_acc)# 每隔2000次训练打印一下日志if total_iter_num % 2000 == 0:temp_loss = total_loss / total_iter_numtemp_acc = total_acc_num / total_iter_numtemp_time = time.time() - start_timeprint('轮次:%d, 损失:%.6f, 时间:%d,准确率:%.3f' %(epoch_idx+1, temp_loss, temp_time, temp_acc))torch.save(my_rnn.state_dict(), './save_model/ai20_rnn_%d.bin'%(epoch_idx+1))# 计算总时间total_time = int(time.time() - start_time)print('训练总耗时:', total_time)# 将结果保存到文件中dict1 = {"avg_loss":total_loss_list,"all_time": total_time,"avg_acc": total_acc_list}with open('./save_results/ai_rnn.json', 'w') as fw:fw.write(json.dumps(dict1))
​return total_loss_list, total_time, total_acc_list

6.2 LSTM模型训练代码实现

基本原理同上

6.3 GRU模型训练代码

基本原理同上

7 模型预测

基本过程

1.获取数据
2.数据预处理:将数据转化one-hot编码
3.实例化模型
4.加载模型训练好的参数: model.load_state_dict(torch.load("model_path"))
5.with torch.no_grad():
6.将数据送入模型进行预测(注意:张量的形状变换)

RNN模型预测代码:

def line2tensor(x):# x-->"bai"tensor_x = torch.zeros(len(x), n_letters)# one-hot表示for li, letter in enumerate(x):tensor_x[li][letters.find(letter)] = 1
​return tensor_x
# 构造rnn预测函数
def rnn_predict(x):# 将数据x进行张量的转换tensor_x =  line2tensor(x)# 加载训练好的模型my_rnn = My_RNN(input_size=57, hidden_size=128, output_size=18)my_rnn.load_state_dict(torch.load('./save_model/ai20_rnn_3.bin'))# 实现模型的预测with torch.no_grad():# 将数据送入模型output, hn = my_rnn(tensor_x, my_rnn.inithidden())print(f'output--》{output}')# 获取output最大的前3个值# output.topk(3, 1, True)values, indexes = torch.topk(output, k=3, dim=-1, largest=True)print(f'values-->{values}')print(f'indexes-->{indexes}')for i in range(3):value = values[0][i]index = indexes[0][i]category = categorys[index]print(f'当前预测的值是:{value}, 国家类别是:{category}')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/86752.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/86752.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙HarmonyOS 关于图片、视频的选择详解

背景 在聊天软件中&#xff0c;发送相册中视频和照片、用相机拍摄视频和图片发送是很常用的功能。在Android和iOS端&#xff0c;大部分应用都通过API方式定义UI来实现相册选择照片、视频&#xff0c;相机拍摄照片、视频&#xff0c;它们一般都支持以下功能&#xff1a; 相册选…

iOS 网络请求断连重试失败?抓包分析丢包原因的完整流程

在移动 App 的开发中&#xff0c;中断网络环境&#xff08;如切换到飞行模式再回网&#xff09;后&#xff0c;App 在重连过程中有时会出现请求未重新发送或丢包的情况。这类问题难重现、难定位&#xff0c;尤其在 iOS 平台上更容易被忽视。我们最近就遇到一个用户反馈“切换网…

使用 DHTMLX Gantt 添加迷你地图:提升大型项目可视化与导航体验

在应对数千个任务构成的大型项目时&#xff0c;DHTMLX Gantt 以其卓越的性能表现和流畅渲染能力广受欢迎。然而&#xff0c;在实际使用中&#xff0c;终端用户往往需要快速定位到时间线中的特定位置&#xff0c;这在面对庞杂任务结构时尤为困难。为此&#xff0c;DHTMLX 提供了…

ROM修改进阶教程------用于自启脚本来打开系统的一些常用开关等指令 备份收藏 【一】

在定制化rom中。有很多项目需要反编译系统的相关应用来实现。但有些功能项完全可以使用指令来更改。那么结合自启脚本就可以很方便的来实现很多功能。网络虽然有很多类似的指令,但一些相关定制化项目的指令很少见而且不全面。此博文将全面收录此类指令。方便rom修改用户借鉴参…

腾讯云TSE注册中心实战:Nacos高可用集群搭建与流量治理避坑指南

1. 为什么选择腾讯云TSE托管Nacos&#xff1f; 在微服务架构中&#xff0c;注册中心承担着服务发现与配置管理的核心职能。Nacos作为阿里开源的动态服务发现组件&#xff0c;已成为国内微服务生态的事实标准。腾讯云微服务引擎TSE&#xff08;Tencent Cloud Service Engine&am…

领域驱动设计(DDD)【26】之CQRS模式初探

文章目录 一 CQRS初探&#xff1a;理解基本概念1.1 什么是CQRS&#xff1f;1.2 CQRS与CRUD的对比1.3 为什么需要CQRS&#xff1f; 二 CQRS深入&#xff1a;架构细节2.1 基本架构组成2.2 数据流示意图 三 CQRS实战&#xff1a;电商订单案例3.1 传统CRUD方式的订单处理3.2 CQRS方…

项目测试-接口测试

软件测试的分类 软件测试主要分硬件和软件 硬件测试: cpu,内存条,显卡...测试可以看得见摸得着的东西 软件测试: web,app,小程序... 测试可以看得见摸不着的东西 web端 web端是在电脑上常常使用的, 也可以称之为网站.(web端是B/S架构) web端的客户端是任何一个访问这个网…

相机的光圈

光圈&#xff08;Aperture&#xff09;是镜头中一个控制光线进入相机的开口&#xff0c;它在摄影中起着至关重要的作用。光圈的大小决定了进入相机传感器的光线数量&#xff0c;并影响曝光、景深、以及拍摄效果。光圈参数通常用f/值&#xff08;光圈值&#xff09;来表示&#…

HarmonyOS NEXT仓颉开发语言实战案例:小而美的旅行App

大家周末好&#xff0c;本文分享一个小而美的旅行app首页&#xff0c;效果图如下&#xff1a; 很显然这个页面还是使用List容器&#xff0c;页面两侧有统一的边距&#xff0c;我们可以在List容器统一设置&#xff1a; List(space:20){ } .padding(left:14,right:14,top:62) .w…

Python银行管理系统01升级(适合初学者)

目录 框架如下: 1. Account类 - 账户数据模型 2. Bank类 - 银行业务逻辑 3. BankApp类 - 图形用户界面 关键概念解析(适合初学者) 1. 面向对象编程(OOP)概念 2. Tkinter GUI编程基础 3. 数据持久化 4. 输入验证 学习建议 系统功能概览 完整代码: 在Python银行…

华为防火墙双向NAT实验

如图所示&#xff0c; 企业内网有一台Server2&#xff0c;通过在FW1上配置nat server&#xff0c;将Server2的www端口映射到了公网&#xff1b; 实验环境中&#xff0c;内网和外网都使用外网的server1提供的DNS服务&#xff0c;在DNS服务器上添加A记录&#xff0c;www.baidu.c…

前端路由的基石:深度剖析 Hash 与 History 模式的本质差异与实战抉择

在单页面应用&#xff08;SPA&#xff09;统治现代Web开发的今天&#xff0c;前端路由已成为构建流畅用户体验的核心技术。而hash和history作为两种主流实现方案&#xff0c;其设计理念和技术细节的差异直接影响着应用架构的选择。本文将深入解析二者的技术本质&#xff0c;通过…

微机系统 - 绪论

绪论: 一:微处理器,微型计算机和微型计算机系统: 分类: 按照系统结构和基本工作原理.计算机分为5大部分:运算器,控制器,存储器,输入设备,输出设备 按照体积,性能和价格分5类:巨型机,大型机,中型机,小型机,微型计算机(单板机,单片机) 微型计算机的特点:集成度高,体积小,重量轻…

基于Java+Springboot的宠物健康咨询系统

源码编号&#xff1a;S564 源码名称&#xff1a;基于Springboot的宠物健康咨询系统 用户类型&#xff1a;多角色&#xff0c;用户、顾问、管理员 数据库表数量&#xff1a;12 张表 主要技术&#xff1a;Java、Vue、ElementUl 、SpringBoot、Maven 运行环境&#xff1a;Win…

SpringBoot+MySQL宠物猫店管理系统

概述 基于SpringBootMySQL开发的宠物猫店管理系统完整源码。该系统功能完善&#xff0c;包含前后台完整功能模块&#xff0c;代码规范易于二次开发&#xff0c;是学习SpringBoot项目实战的优秀范例。 主要内容 前台功能展示 系统前台设计简洁实用&#xff0c;主要包含以下核…

UE5 - 制作《塞尔达传说》中林克的技能 - 16 - 遥控炸弹(一)

让我们继续《塞尔达传说》中林克技能的制作&#xff01;&#xff01;&#xff01; 本章节的核心目标&#xff1a;素材导入与遥控炸弹的外观 先让我们看一下完成后的效果&#xff1a; 基本流程&#xff1a;素材准备->C类开发->蓝图配置->场景部署 1.素材准备&#xff1…

HTTP中常见的Content-Type

Content-Type&#xff0c;也称为互联网媒体类型或MIME类型&#xff0c;是HTTP协议中的一个头部字段&#xff0c;用于指定处理请求和响应中的媒体类型信息。它告诉服务器如何处理请求的数据&#xff0c;同时也指导客户端&#xff08;通常是浏览器&#xff09;如何解析响应的数据…

Android11 wifi开启源码分析

目录 一、APP层源码分析 1.1、寻找页面activity 1.2、寻找页面开关按钮布局 二&#xff0c;framework层代码分析 2.1 开启wifi入口 2.2 WiFiNative 三&#xff0c;HAL层代码分析 这段时间撸了WIFI开启流程源码&#xff0c;本着前人栽树后人乘凉的原则&#xff0c;有志于…

R语言使用nonrandom包进行倾向评分匹配

倾向评分匹配&#xff08;Propensity Score Matching&#xff0c;简称PSM&#xff09;是一种统计学方法&#xff0c;用于处理观察研究&#xff08;Observational Study&#xff09;的数据&#xff0c;在SCI文章中应用非常广泛。在观察研究中&#xff0c;由于种种原因&#xff0…

LeetCode Hot 100 找到字符串中所有字母异位词

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 示例 1: 输入: s "cbaebabacd", p "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "a…