当前位置: 首页 > news >正文

深度学习每周学习总结N6:使用Word2vec实现文本分类

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊 | 接辅导、项目定制

目录

    • 0. 总结:
      • 1.加载数据
      • 2. 构建词典
      • 3. 生成数据批次和迭代器
      • 4.模型搭建及初始化
      • 5. 定义训练与评估函数
      • 6. 拆分数据集并运行模型
      • 7. 结果可视化
      • 8. 测试指定数据
      • 9. 提问:与N4文本分类的异同
        • 分析代码
        • 数据处理方式
          • 1. 分词处理
          • 2. 构建词典
          • 3. 向量表示
        • 值得关注的点
          • 1. Word2Vec模型训练
          • 2. 文本向量化
          • 3. 数据加载和批处理
        • 模型结构
          • 1. 模型定义
        • 训练和评估
          • 1. 训练过程
        • 异同点总结
          • 共同点
          • 不同点
        • 值得学习的点

0. 总结:

之前有学习过文本预处理的环节,对文本处理的主要方式有以下三种:

1:词袋模型(one-hot编码)

2:TF-IDF

3:Word2Vec(词向量(Word Embedding) 以及Word2vec(Word Embedding 的方法之一))

详细介绍及中英文分词详见pytorch文本分类(一):文本预处理

上上上上期主要介绍Embedding,及EmbeddingBag 使用示例(对词索引向量转化为词嵌入向量) ,上上上期主要介绍:应用三种模型的英文分类

上上期将主要介绍中文基本分类(熟悉流程)、拓展:textCNN分类(通用模型)、拓展:Bert分类(模型进阶)

上期主要介绍Word2Vec,和nn.Embedding(), nn.EmbeddingBag()相比都是嵌入技术,用于将离散的词语或符号映射到连续的向量空间。

nn.Embeddingnn.EmbeddingBag 是深度学习框架(如 PyTorch)中的层,直接用于神经网络模型中,而 Word2Vec 是一种独立的词嵌入算法。

使用来说,如果需要在神经网络中处理变长序列的嵌入,可以选择 nn.EmbeddingBag;如果需要预训练词嵌入用于不同任务,可以选择 Word2Vec

本期主要介绍使用Word2Vec实现文本分类:

与N4文本分类的异同点总结

  • 共同点
    数据加载:都使用了PyTorch的DataLoader来批量加载数据。
    模型训练:训练过程大同小异,都是前向传播、计算损失、反向传播和梯度更新。
  • 不同点
    分词处理
    BERT模型:使用专门的BERT分词器。
    传统嵌入方法:通常使用jieba等工具进行分词。
    Word2Vec模型:假设数据已经分词。
    词向量表示
    BERT模型:使用BERT生成的上下文相关的词向量。
    传统嵌入方法:使用静态预训练词向量。
    Word2Vec模型:训练一个Word2Vec模型生成词向量。
    模型结构
    BERT模型:使用预训练的BERT模型作为编码器。
    传统嵌入方法:一般使用嵌入层+卷积/循环神经网络。
    Word2Vec模型:使用Word2Vec词向量和一个简单的线性分类器。
  • 值得学习的点
    词向量的使用:了解如何使用Word2Vec生成词向量并将其用于下游任务。
    数据预处理:不同方法的数据预处理方式,尤其是分词和词向量化的处理。
    模型训练:标准的模型训练和评估流程,尤其是损失计算、反向传播和梯度更新等步骤。
    超参数选择:注意学习率、批量大小和训练轮数等超参数的选择。
    通过这些比较和分析,可以更好地理解不同文本分类方法的优缺点以及适用场景。

1.加载数据

import torch
import torch.nn as nn
import warningsfrom torch.utils.data import DataLoader,Dataset,random_splitimport pandas as pdwarnings.filterwarnings('ignore')# 忽略警告信息
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device
device(type='cpu')
train_data = pd.read_csv('./data/N6-train.csv',sep='\t',header=None)
train_data
01
0还有双鸭山到淮阴的汽车票吗13号的Travel-Query
1从这里怎么回家Travel-Query
2随便播放一首专辑阁楼里的佛里的歌Music-Play
3给看一下墓王之王嘛FilmTele-Play
4我想看挑战两把s686打突变团竞的游戏视频Video-Play
.........
12095一千六百五十三加三千一百六十五点六五等于几Calendar-Query
12096稍小点客厅空调风速HomeAppliance-Control
12097黎耀祥陈豪邓萃雯畲诗曼陈法拉敖嘉年杨怡马浚伟等到场出席Radio-Listen
12098百事盖世群星星光演唱会有谁Video-Play
12099下周一视频会议的闹钟帮我开开Alarm-Update

12100 rows × 2 columns

class CustomDataset(Dataset):def __init__(self,texts,labels):self.texts = textsself.labels = labelsdef __len__(self):return len(self.texts)def __getitem__(self,idx):return self.texts[idx],self.labels[idx]x = train_data[0].values[:]
y = train_data[1].values[:]

2. 构建词典

from gensim.models.word2vec import Word2Vec
import numpy as np# 训练Word2Vec 浅层神经网络模型
w2v = Word2Vec(vector_size = 100, # 指特征向量的维度,默认为100min_count = 3      # 可以对字典做截断,词频少于min_count次数的单词会被丢弃掉,默认值为5
)
w2v.build_vocab(x)
w2v.train(x,total_examples = w2v.corpus_count,epochs = 20
)
(2733133, 3663560)

Word2Vec可以直接训练模型,一步到位。这里分了三步


第一步构建一个空模型


第二步使用 build_vocab 方法根据输入的文本数据 x 构建词典。build_vocab 方法会统计输入文本中每个词汇出现的次数,并按照词频从高到低的顺序将词汇加入词典中。


第三步使用 train 方法对模型进行训练,total_examples 参数指定了训练时使用的文本数量,这里使用的是 w2v.corpus_count 属性,表示输入文本的数量

如果一步到位的话代码为:

w2v = Word2Vec(x,vector_size=100,min_count=3,epochs=20)
# 将文本转化为向量
def average_vec(text):vec = np.zeros(100).reshape((1,100))for word in text:try:vec+= w2v.wv[word].reshape((1,100))except KeyError:continuereturn vec
# 将词向量保存为Ndarray
x_vec = np.concatenate([average_vec(z) for z in x])# 保存 Word2Vec 模型及词向量
w2v.save('data/w2v_model.pkl')

这段代码定义了一个函数 average_vec(text),它接受一个包含多个词的列表 text 作为输入,并返回这些词对应词向量的平均值。该函数


首先初始化一个形状为 (1, 100) 的全零 numpy 数组来表示平均向量


然后遍历 text 中的每个词,并尝试从 Word2Vec 模型 w2v 中使用 wv 属性获取其对应的词向量。如果在模型中找到了该词,函数将其向量加到 vec 中。如果未找到该词,函数会继续迭代下一个词


最后,函数返回平均向量 vec

然后使用列表推导式将 average_vec() 函数应用于列表 x 中的每个元素。得到的平均向量列表使用 np.concatenate() 连接成一个 numpy 数组 x_vec,该数组表示 x 中所有元素的平均向量。x_vec 的形状为 (n, 100),其中 n 是 x 中元素的数量。

train_iter = CustomDataset(x_vec,y)
len(x),len(x_vec)
(12100, 12100)
label_name = list(set(train_data[1].values[:]))
label_name
['Audio-Play','Radio-Listen','Travel-Query','TVProgram-Play','Video-Play','Other','Music-Play','Alarm-Update','Weather-Query','Calendar-Query','FilmTele-Play','HomeAppliance-Control']

3. 生成数据批次和迭代器

text_pipeline = lambda x:average_vec(x)
label_pipeline = lambda x:label_name.index(x)

lambda 表达式的语法为:lambda arguments: expression

其中 arguments 是函数的参数,可以有多个参数,用逗号分隔。expression 是一个表达式,它定义了函数的返回值。


text_pipeline 函数:接受一个包含多个词的列表 x 作为输入,并返回这些词对应词向量的平均值,即调用了之前定义的 average_vec 函数。这个函数用于将原始文本数据转换为词向量平均值表示的形式。


label_pipeline 函数:接受一个标签名 x 作为输入,并返回该标签名label_name 列表中的索引。这个函数可以用于将原始标签数据转换为数字索引表示的形式。

text_pipeline("你在干嘛")
array([[ 0.18396786,  0.24416898,  1.0977701 , -0.01573543, -2.34493342,0.14462006,  1.09909924,  0.72848918,  0.43942198, -0.36590184,-1.64841774, -4.0461483 ,  0.52117442, -0.93646932,  0.65021983,1.8863973 ,  3.21435681, -2.37889412,  4.38834111, -1.31753699,3.04073831, -1.56194845, -0.01702204, -0.26231994, -0.57977456,-0.59202761, -2.09177154, -0.93535494,  2.01195888, -2.03609261,2.05241142,  1.59645403, -0.09730234, -1.39273715,  0.35281967,0.07837144, -0.28004935,  3.58092116, -2.69026518,  1.25223976,0.26495122,  0.6858208 , -0.26904737,  1.87308259,  0.21092373,0.18170499,  2.06353265, -0.55430332, -2.19337641,  1.70870071,0.30772619, -2.93958779, -0.97791416,  0.84643018, -0.75232047,-0.05284989,  1.25495276, -0.59528226, -0.44547228,  0.5255087 ,0.86212044, -1.28084296,  2.38232671, -0.28152593, -0.64530418,1.20289534, -0.42659164,  0.95189874,  1.22811846,  0.04619575,-0.50489213,  0.27519883,  2.16535747, -0.17590564,  0.42813917,-0.09275024, -3.63906508,  0.53455901,  2.07623281, -0.39027843,-1.92971458,  0.42125694, -2.16986141,  2.77343564, -1.69743965,-1.50195191,  1.76218376, -1.49796952, -0.26446094, -0.30981308,0.9657587 , -1.63175048,  1.01593184,  0.9586434 ,  0.70671193,-2.49167663,  0.37804852,  0.94789429, -1.52659395,  0.88284026]])
label_pipeline("Travel-Query")
2
from torch.utils.data import DataLoaderdef collate_batch(batch):label_list,text_list = [],[]for (_text,_label) in batch:# 标签列表label_list.append(label_pipeline(_label))# 文本列表processed_text = torch.tensor(text_pipeline(_text),dtype=torch.float32)text_list.append(processed_text)label_list = torch.tensor(label_list,dtype = torch.int64)text_list = torch.cat(text_list)return text_list.to(device),label_list.to(device)

4.模型搭建及初始化

from torch import nnclass TextClassificationModel(nn.Module):def __init__(self,num_class):super(TextClassificationModel,self).__init__()self.fc = nn.Linear(100,num_class)def forward(self,text):return self.fc(text)
# 模型初始化
num_class = len(label_name)
vocab_size = 100000
em_size = 12
model = TextClassificationModel(num_class).to(device)

5. 定义训练与评估函数

def train(dataloader):model.train()  # 切换为训练模式total_acc, total_loss, total_count = 0, 0, 0for idx, (text,label) in enumerate(dataloader):pred = model(text)optimizer.zero_grad()                    # grad属性归零loss = criterion(pred, label) # 计算网络输出和真实值之间的差距,label为真实值loss.backward()                          # 反向传播torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1) # 梯度裁剪optimizer.step()  # 每一步自动更新# 记录acc与losstotal_acc   += (pred.argmax(1) == label).sum().item()total_loss  += loss.item()total_count += label.size(0)return total_acc / total_count, total_loss / total_countdef evaluate(dataloader):model.eval()  # 切换为测试模式total_acc, total_loss, total_count = 0, 0, 0with torch.no_grad():for idx, (text,label) in enumerate(dataloader):pred = model(text)loss = criterion(pred, label)  # 计算loss值# 记录测试数据total_acc   += (pred.argmax(1) == label).sum().item()total_loss  += loss.item()total_count += label.size(0)return total_acc/total_count, total_loss/total_count

torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1)是一个PyTorch函数,用于在训练神经网络时限制梯度的大小。这种操作被称为梯度裁剪(gradient clipping),可以防止梯度爆炸问题,从而提高神经网络的稳定性和性能。

在这个函数中:


model.parameters()表示模型的所有参数。对于一个神经网络,参数通常包括权重和偏置项。


0.1是一个指定的阈值,表示梯度的最大范数(L2范数)。如果计算出的梯度范数超过这个阈值,梯度会被缩放,使其范数等于阈值。

梯度裁剪的主要目的是防止梯度爆炸。梯度爆炸通常发生在训练深度神经网络时,尤其是在处理长序列数据的循环神经网络(RNN)中。当梯度爆炸时,参数更新可能会变得非常大,导致模型无法收敛或出现数值不稳定。通过限制梯度的大小,梯度裁剪有助于解决这些问题,使模型训练变得更加稳定。

6. 拆分数据集并运行模型

# 超参数
EPOCHS = 10 # epoch
LR = 5 # 学习率
BATCH_SIZE = 64 # batch size for trainingcriterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=LR)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.1)
total_accu = None# 构建数据集
train_dataset = CustomDataset(train_data[0].values[:], train_data[1].values[:])split_train_, split_valid_ = random_split(train_dataset,[int(len(train_dataset)*0.8), len(train_dataset) - int(len(train_dataset)*0.8)]
)
train_dataloader = DataLoader(split_train_, batch_size=BATCH_SIZE,shuffle=True, collate_fn=collate_batch)
valid_dataloader = DataLoader(split_valid_, batch_size=BATCH_SIZE,shuffle=True, collate_fn=collate_batch)
import copy
import time
epochs     = 10train_loss = []
train_acc  = []
test_loss  = []
test_acc   = []best_acc = 0    # 设置一个最佳准确率,作为最佳模型的判别指标for epoch in range(epochs):# 更新学习率(使用自定义学习率时使用)# adjust_learning_rate(optimizer, epoch, learn_rate)epoch_start_time = time.time()epoch_train_acc, epoch_train_loss = train(train_dataloader)scheduler.step() # 更新学习率(调用官方动态学习率接口时使用)epoch_test_acc, epoch_test_loss = evaluate(valid_dataloader)# 保存最佳模型到 best_modelif epoch_test_acc > best_acc:best_acc   = epoch_test_accbest_model = copy.deepcopy(model)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)# 获取当前的学习率lr = optimizer.state_dict()['param_groups'][0]['lr']template = ('Epoch:{:2d}, time: {:4.2f}s,Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')print(template.format(epoch+1,time.time() - epoch_start_time,epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss, lr))# 保存最佳模型到文件中
PATH = './best_model.pth'  # 保存的参数文件名
torch.save(model.state_dict(), PATH)print('Done')
Epoch: 1, time: 0.94s,Train_acc:79.9%, Train_loss:0.021, Test_acc:85.5%, Test_loss:0.015, Lr:5.00E-01
Epoch: 2, time: 0.91s,Train_acc:88.8%, Train_loss:0.009, Test_acc:87.8%, Test_loss:0.009, Lr:5.00E-02
Epoch: 3, time: 0.91s,Train_acc:90.1%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-03
Epoch: 4, time: 0.95s,Train_acc:90.1%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-04
Epoch: 5, time: 0.91s,Train_acc:90.2%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.009, Lr:5.00E-05
Epoch: 6, time: 0.88s,Train_acc:90.2%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-06
Epoch: 7, time: 0.87s,Train_acc:90.2%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-07
Epoch: 8, time: 0.87s,Train_acc:90.2%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-08
Epoch: 9, time: 0.91s,Train_acc:90.2%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-09
Epoch:10, time: 0.92s,Train_acc:90.2%, Train_loss:0.007, Test_acc:88.1%, Test_loss:0.008, Lr:5.00E-10
Done

7. 结果可视化

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率epochs_range = range(epochs)plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()


在这里插入图片描述

8. 测试指定数据

def predict(text, text_pipeline):with torch.no_grad():text = torch.tensor(text_pipeline(text), dtype=torch.float32)print(text.shape)output = model(text)return output.argmax(1).item()# ex_text_str = "随便播放一首专辑阁楼里的佛里的歌"
ex_text_str = "还有双鸭山到淮阴的汽车票吗13号的"model = model.to("cpu")print("该文本的类别是:%s" %label_name[predict(ex_text_str, text_pipeline)])
torch.Size([1, 100])
该文本的类别是:Travel-Query

9. 提问:与N4文本分类的异同

分析代码

代码使用了Word2Vec技术将文本转化为词向量,并通过一个简单的线性分类器进行文本分类。以下是详细的分析和与之前两个模型(BERT和基于传统嵌入的方法)处理数据的方式的比较:

数据处理方式
1. 分词处理
  • BERT模型:使用了BERT的分词器(BertTokenizer),能够处理中文字符,并将其转换为BERT所需的输入格式,包括input_idsattention_mask
  • 传统嵌入方法:一般使用jieba进行分词,并通过词汇表将分词结果转换为索引。
  • Word2Vec模型:在代码中直接将句子传递给Word2Vec进行训练,假设句子已经被分好词。
2. 构建词典
  • BERT模型:直接使用预训练模型提供的词典(如bert-base-chinese)。
  • 传统嵌入方法:手动构建词汇表,并将词汇映射为索引。
  • Word2Vec模型:通过训练Word2Vec模型来构建词向量的词典。
3. 向量表示
  • BERT模型:使用BERT生成的上下文相关的词向量。
  • 传统嵌入方法:通常使用静态的预训练词向量(如Word2Vec或GloVe)。
  • Word2Vec模型:代码中训练了一个浅层的Word2Vec模型,并将每个词转换为一个100维的向量,句子表示为这些词向量的平均值。
值得关注的点
1. Word2Vec模型训练
from gensim.models.word2vec import Word2Vec
import numpy as np# 训练Word2Vec 浅层神经网络模型
w2v = Word2Vec(vector_size=100,  # 指特征向量的维度min_count=3       # 词频少于min_count次数的单词会被丢弃
)
w2v.build_vocab(x)
w2v.train(x, total_examples=w2v.corpus_count, epochs=20)
  • 训练Word2Vec模型,这里使用的是Gensim库。vector_size参数设置了词向量的维度,min_count参数设置了词频少于3次的单词会被丢弃。
2. 文本向量化
# 将文本转化为向量
def average_vec(text):vec = np.zeros(100).reshape((1, 100))for word in text:try:vec += w2v.wv[word].reshape((1, 100))except KeyError:continuereturn vec
# 将词向量保存为Ndarray
x_vec = np.concatenate([average_vec(z) for z in x])
  • 使用Word2Vec模型将每个词转换为向量,并计算句子的平均向量。这里的句子表示为其包含词的词向量的平均值。
3. 数据加载和批处理
from torch.utils.data import DataLoaderdef collate_batch(batch):label_list, text_list = [], []for (_text, _label) in batch:# 标签列表label_list.append(label_pipeline(_label))# 文本列表processed_text = torch.tensor(text_pipeline(_text), dtype=torch.float32)text_list.append(processed_text)label_list = torch.tensor(label_list, dtype=torch.int64)text_list = torch.cat(text_list)return text_list.to(device), label_list.to(device)
  • 这里的collate_batch函数用于将数据批处理成训练时所需的格式。text_pipelinelabel_pipeline分别用于处理文本和标签。
模型结构
1. 模型定义
class TextClassificationModel(nn.Module):def __init__(self, num_class):super(TextClassificationModel, self).__init__()self.fc = nn.Linear(100, num_class)def forward(self, text):return self.fc(text)
  • 这是一个简单的线性分类器,输入是100维的词向量,输出是分类的结果。
训练和评估
1. 训练过程
def train(dataloader):model.train()  # 切换为训练模式total_acc, total_loss, total_count = 0, 0, 0for idx, (text, label) in enumerate(dataloader):pred = model(text)optimizer.zero_grad()                    # grad属性归零loss = criterion(pred, label) # 计算网络输出和真实值之间的差距,label为真实值loss.backward()                          # 反向传播torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1) # 梯度裁剪optimizer.step()  # 每一步自动更新# 记录acc与losstotal_acc += (pred.argmax(1) == label).sum().item()total_loss += loss.item()total_count += label.size(0)return total_acc / total_count, total_loss / total_count
  • 标准的训练过程,包括前向传播、计算损失、反向传播和梯度更新。
异同点总结
共同点
  1. 数据加载:都使用了PyTorch的DataLoader来批量加载数据。
  2. 模型训练:训练过程大同小异,都是前向传播、计算损失、反向传播和梯度更新。
不同点
  1. 分词处理

    • BERT模型:使用专门的BERT分词器。
    • 传统嵌入方法:通常使用jieba等工具进行分词。
    • Word2Vec模型:假设数据已经分词。
  2. 词向量表示

    • BERT模型:使用BERT生成的上下文相关的词向量。
    • 传统嵌入方法:使用静态预训练词向量。
    • Word2Vec模型:训练一个Word2Vec模型生成词向量。
  3. 模型结构

    • BERT模型:使用预训练的BERT模型作为编码器。
    • 传统嵌入方法:一般使用嵌入层+卷积/循环神经网络。
    • Word2Vec模型:使用Word2Vec词向量和一个简单的线性分类器。
值得学习的点
  1. 词向量的使用:了解如何使用Word2Vec生成词向量并将其用于下游任务。
  2. 数据预处理:不同方法的数据预处理方式,尤其是分词和词向量化的处理。
  3. 模型训练:标准的模型训练和评估流程,尤其是损失计算、反向传播和梯度更新等步骤。
  4. 超参数选择:注意学习率、批量大小和训练轮数等超参数的选择。

通过这些比较和分析,可以更好地理解不同文本分类方法的优缺点以及适用场景。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Spring Cloud全解析:注册中心之zookeeper注册中心
  • 4.MySQL数据类型
  • 2023华为od机试C卷【围棋的气】python实现
  • 哈萨克语驾考学习软件求推荐?
  • Springboot项目基础开发模式+注解
  • 【香橙派系列教程】(十三) 香橙派的摄像头接入
  • 【Pyspark-驯化】一文搞懂Pyspark修改hive表描述以及增加列使用技巧
  • 简单的射箭小游戏网页源码
  • 表字段显示tip
  • 【数据结构题目】循环队列,以及队列实现栈的模拟
  • C语言 | Leetcode C语言题解之第332题重新安排行程
  • Android 下载安装配置
  • 探索Python模块搜索路径的奥秘
  • Python中的责任链模式:构建灵活的请求处理机制
  • Python知识点:使用FastAI进行快速深度学习模型构建
  • [nginx文档翻译系列] 控制nginx
  • CentOS7简单部署NFS
  • CODING 缺陷管理功能正式开始公测
  • Java教程_软件开发基础
  • Kibana配置logstash,报表一体化
  • PermissionScope Swift4 兼容问题
  • Redis 懒删除(lazy free)简史
  • Theano - 导数
  • 安装python包到指定虚拟环境
  • 电商搜索引擎的架构设计和性能优化
  • 翻译:Hystrix - How To Use
  • 技术:超级实用的电脑小技巧
  • 近期前端发展计划
  • 力扣(LeetCode)22
  • 你不可错过的前端面试题(一)
  • 手写双向链表LinkedList的几个常用功能
  • 数据可视化之 Sankey 桑基图的实现
  • 思维导图—你不知道的JavaScript中卷
  • 微服务框架lagom
  • 一个SAP顾问在美国的这些年
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 原生Ajax
  • 怎么把视频里的音乐提取出来
  • 栈实现走出迷宫(C++)
  • ​MySQL主从复制一致性检测
  • ​力扣解法汇总946-验证栈序列
  • ​人工智能书单(数学基础篇)
  • #laravel部署安装报错loadFactoriesFrom是undefined method #
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (九十四)函数和二维数组
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (转)创业家杂志:UCWEB天使第一步
  • (转)四层和七层负载均衡的区别
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .Net Core 微服务之Consul(二)-集群搭建
  • .net framework 4.8 开发windows系统服务
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .NET 解决重复提交问题
  • .NET 事件模型教程(二)