基于LSTM的文本分类2——文本数据处理
前言
由于计算机无法认识到文字内容,因此在训练模型时需要将文字映射到计算机能够识别的编码内容。
映射的流程如下:
- 首先将文字内容按照词表映射到成唯一的数字ID。比如“我爱中国”,将“中”映射为1,将“国”映射到2。
- 再将文字映射到的数字ID映射成向量。比如“中”映射成了ID=1,再将1映射成某个向量,比如[0,0,0,0,0,1]。
# 构建数据集:词汇表、训练集、验证集、测试集vocab, train_data, dev_data, test_data = build_dataset(config, args.word)# 构建数据迭代器,用于批量加载数据train_iter = build_iterator(train_data, config)dev_iter = build_iterator(dev_data, config)test_iter = build_iterator(test_data, config)
这篇文章的目的就是搞懂上面的代码如何实现,即如何构建文字数据集和迭代器。
源码
# coding: UTF-8
# coding: UTF-8
# 导入必要的库
import os # 操作系统接口,用于文件路径处理
import torch # PyTorch深度学习框架
import numpy as np # 数值计算库
import pickle as pkl # 对象序列化/反序列化,用于保存词汇表
from tqdm import tqdm # 进度条显示
import time
from datetime import timedelta# 全局常量定义
MAX_VOCAB_SIZE = 10000 # 词汇表最大容量限制
UNK, PAD = '<UNK>', '<PAD>' # 特殊标记:未知词(UNK)和填充符(PAD)def build_vocab(file_path, tokenizer, max_size, min_freq):"""构建词汇表字典Args:file_path: 训练集文件路径tokenizer: 分词函数(按词或字符分割)max_size: 最大词汇表大小min_freq: 词的最小出现频次阈值Returns:vocab_dic: 词到索引的映射字典,包含UNK和PAD"""vocab_dic = {}# 遍历训练集文件的每一行with open(file_path, 'r', encoding='UTF-8') as f:for line in tqdm(f): # 使用tqdm显示进度条lin = line.strip()if not lin:continue # 跳过空行content = lin.split('\t') # 分割文本和标签,取文本内容# 分词并统计词频for word in tokenizer(content):vocab_dic[word] = vocab_dic.get(word, 0) + 1# 筛选词频≥min_freq的词,按词频降序排列,取前max_size个vocab_list = sorted([item for item in vocab_dic.items() if item >= min_freq],key=lambda x: x, reverse=True)[:max_size]# 生成词到索引的映射字典vocab_dic = {word_count: idx for idx, word_count in enumerate(vocab_list)}# 添加未知词和填充符的索引(排在最后两位)vocab_dic.update({UNK: len(vocab_dic), PAD: len(vocab_dic) + 1})return vocab_dicdef build_dataset(config, use_word):"""构建数据集Args:config: 配置对象,包含文件路径等参数use_word: 分词方式,True表示按词分割,False按字符分割Returns:vocab: 词汇表字典train/dev/test: 处理后的数据集"""# 定义分词器if use_word:tokenizer = lambda x: x.split(' ') # 按空格分割(词级别)else:tokenizer = lambda x: [y for y in x] # 按字符分割# 加载或创建词汇表if os.path.exists(config.vocab_path):vocab = pkl.load(open(config.vocab_path, 'rb')) # 从文件加载else:vocab = build_vocab(config.train_path, tokenizer, MAX_VOCAB_SIZE, min_freq=1)pkl.dump(vocab, open(config.vocab_path, 'wb')) # 保存词汇表print(f"Vocab size: {len(vocab)}")# -----------------------n-gram哈希函数定义-----------------------def biGramHash(sequence, t, buckets):"""计算第t个位置的bigram哈希值公式: (前一个词的哈希值 * 质数) % 桶大小说明:如果t-1越界,用0代替(相当于用PAD的哈希值)"""t1 = sequence[t - 1] if t - 1 >= 0 else 0return (t1 * 14918087) % buckets # 14918087是一个大质数def triGramHash(sequence, t, buckets):"""计算第t个位置的trigram哈希值公式: (前两个词的哈希值组合 * 质数) % 桶大小说明:如果t-1或t-2越界,用0代替"""t1 = sequence[t - 1] if t - 1 >= 0 else 0t2 = sequence[t - 2] if t - 2 >= 0 else 0return (t2 * 14918087 * 18408749 + t1 * 14918087) % buckets # 双质数减少冲突# -------------------------------------------------------------def load_dataset(path, pad_size=32):"""加载并处理单个数据集文件Args:path: 数据集文件路径pad_size: 填充/截断后的固定长度Returns:contents: 处理后的数据列表,元素为(词索引, 标签, 长度, bigram, trigram)"""contents = []with open(path, 'r', encoding='UTF-8') as f:for line in tqdm(f): # 显示进度条lin = line.strip()if not lin:continuecontent, label = lin.split('\t') # 分割文本和标签# 分词并处理长度token = tokenizer(content)seq_len = len(token)# 填充或截断至固定长度pad_sizeif pad_size:if len(token) < pad_size:# 注意:这里用vocab.get(PAD)可能存在错误,PAD应为词汇表中已存在的键token.extend([vocab.get(PAD)] * (pad_size - len(token))) # 填充else:token = token[:pad_size] # 截断seq_len = pad_size # 更新实际长度为pad_size# 将词转换为索引,未知词用UNK的索引words_line = []for word in token:words_line.append(vocab.get(word, vocab.get(UNK))) # 双重保险取UNK索引# 生成n-gram特征(FastText模型需要)buckets = config.n_gram_vocab # 从配置获取哈希桶数量bigram = []trigram = []for i in range(pad_size):# 为每个位置生成bigram和trigram的哈希值bigram.append(biGramHash(words_line, i, buckets))trigram.append(triGramHash(words_line, i, buckets))# 添加处理后的数据:词索引、标签、长度、bigram、trigramcontents.append((words_line, int(label), seq_len, bigram, trigram))return contents # 返回结构:[([...], 0, 32, [...], [...]), ...]# 加载并处理所有数据集train = load_dataset(config.train_path, config.pad_size)dev = load_dataset(config.dev_path, config.pad_size)test = load_dataset(config.test_path, config.pad_size)return vocab, train, dev, testclass DatasetIterater(object):"""数据集迭代器,用于按批次生成数据"""def __init__(self, batches, batch_size, device):"""Args:batches: 处理后的数据集,格式为[(words_line, label, seq_len, bigram, trigram), ...]batch_size: 每个批次的样本数device: 数据存放设备(cpu或cuda)"""self.batch_size = batch_sizeself.batches = batchesself.n_batches = len(batches) // batch_size # 完整批次数self.residue = False # 是否包含不完整的剩余批次# 如果总样本数不能被batch_size整除,设置residue标志if len(batches) % self.n_batches != 0:self.residue = Trueself.index = 0 # 当前批次索引self.device = device # 设备类型def _to_tensor(self, datas):"""将原始数据转换为Tensor格式"""# 注释掉的代码为按序列长度排序的逻辑(可用于动态padding优化)# xx = [xxx for xxx in datas] # 获取所有样本的原始长度# indexx = np.argsort(xx)[::-1] # 按长度降序排列的索引# datas = np.array(datas)[indexx] # 重新排列数据# 构造各特征张量(LongTensor用于整型数据)x = torch.LongTensor([_ for _ in datas]).to(self.device) # 词索引序列y = torch.LongTensor([_ for _ in datas]).to(self.device) # 标签bigram = torch.LongTensor([_ for _ in datas]).to(self.device) # bigram特征trigram = torch.LongTensor([_ for _ in datas]).to(self.device) # trigram特征# 实际长度(考虑padding前的原始长度,但不超过pad_size)seq_len = torch.LongTensor([_ for _ in datas]).to(self.device)return (x, seq_len, bigram, trigram), y # 返回特征元组和标签def __next__(self):"""生成下一个批次数据"""# 处理剩余的不完整批次(当总样本数不是batch_size整数倍时)if self.residue and self.index == self.n_batches:batches = self.batches[self.index * self.batch_size: len(self.batches)]self.index += 1batches = self._to_tensor(batches)return batches# 所有批次处理完成后重置索引并抛出停止迭代异常elif self.index >= self.n_batches:self.index = 0raise StopIteration# 正常批次处理else:batches = self.batches[self.index * self.batch_size: (self.index + 1) * self.batch_size]self.index += 1batches = self._to_tensor(batches)return batchesdef __iter__(self):"""返回迭代器自身"""return selfdef __len__(self):"""返回总批次数(包含剩余批次)"""return self.n_batches + 1 if self.residue else self.n_batchesdef build_iterator(dataset, config):"""构建数据集迭代器Args:dataset: 处理后的数据集config: 配置对象,需包含batch_size和device属性Returns:DatasetIterater实例"""iter = DatasetIterater(dataset, config.batch_size, config.device)return iterdef get_time_dif(start_time):"""计算时间间隔Args:start_time: 开始时间戳Returns:timedelta: 格式化的时间差(秒级精度)示例:>>> start = time.time()>>> # 执行操作...>>> print(get_time_dif(start)) # 输出: 0:00:12"""end_time = time.time()time_dif = end_time - start_timereturn timedelta(seconds=int(round(time_dif)))if __name__ == "__main__":"""预训练词向量提取(示例用法)"""# 文件路径配置vocab_dir = "./THUCNews/data/vocab.pkl" # 词汇表路径pretrain_dir = "./THUCNews/data/sgns.sogou.char" # 预训练向量路径filename_trimmed_dir = "./THUCNews/data/vocab.embedding.sougou" # 输出路径emb_dim = 300 # 词向量维度# 加载词汇表(词到id的映射字典)word_to_id = pkl.load(open(vocab_dir, 'rb'))# 初始化随机词向量矩阵(词汇表大小 x 维度)embeddings = np.random.rand(len(word_to_id), emb_dim)# 加载预训练词向量with open(pretrain_dir, "r", encoding='UTF-8') as f:for i, line in enumerate(f.readlines()):# 跳过首行标题(如果存在)# if i == 0: continuelin = line.strip().split(" ")word = lin # 词vector = lin[1:301] # 对应向量# 如果当前词在词汇表中,更新其向量if word in word_to_id:idx = word_to_id[word]emb = [float(x) for x in vector] # 转换为浮点数列表embeddings[idx] = np.asarray(emb, dtype='float32') # 更新矩阵# 保存压缩后的词向量矩阵(npz格式)np.savez_compressed(filename_trimmed_dir, embeddings=embeddings)
数据集构建
def build_dataset(config, use_word):"""构建数据集Args:config: 配置对象,包含文件路径等参数use_word: 分词方式,True表示按词分割,False按字符分割Returns:vocab: 词汇表字典train/dev/test: 处理后的数据集"""# 定义分词器if use_word:tokenizer = lambda x: x.split(' ') # 按空格分割(词级别)else:tokenizer = lambda x: [y for y in x] # 按字符分割# 加载或创建词汇表if os.path.exists(config.vocab_path):vocab = pkl.load(open(config.vocab_path, 'rb')) # 从文件加载else:vocab = build_vocab(config.train_path, tokenizer, MAX_VOCAB_SIZE, min_freq=1)pkl.dump(vocab, open(config.vocab_path, 'wb')) # 保存词汇表print(f"Vocab size: {len(vocab)}")# -----------------------n-gram哈希函数定义-----------------------def biGramHash(sequence, t, buckets):"""计算第t个位置的bigram哈希值公式: (前一个词的哈希值 * 质数) % 桶大小说明:如果t-1越界,用0代替(相当于用PAD的哈希值)"""t1 = sequence[t - 1] if t - 1 >= 0 else 0return (t1 * 14918087) % buckets # 14918087是一个大质数def triGramHash(sequence, t, buckets):"""计算第t个位置的trigram哈希值公式: (前两个词的哈希值组合 * 质数) % 桶大小说明:如果t-1或t-2越界,用0代替"""t1 = sequence[t - 1] if t - 1 >= 0 else 0t2 = sequence[t - 2] if t - 2 >= 0 else 0return (t2 * 14918087 * 18408749 + t1 * 14918087) % buckets # 双质数减少冲突# -------------------------------------------------------------def load_dataset(path, pad_size=32):"""加载并处理单个数据集文件Args:path: 数据集文件路径pad_size: 填充/截断后的固定长度Returns:contents: 处理后的数据列表,元素为(词索引, 标签, 长度, bigram, trigram)"""contents = []with open(path, 'r', encoding='UTF-8') as f:for line in tqdm(f): # 显示进度条lin = line.strip()if not lin:continuecontent, label = lin.split('\t') # 分割文本和标签# 分词并处理长度token = tokenizer(content)seq_len = len(token)# 填充或截断至固定长度pad_sizeif pad_size:if len(token) < pad_size:# 注意:这里用vocab.get(PAD)可能存在错误,PAD应为词汇表中已存在的键token.extend([vocab.get(PAD)] * (pad_size - len(token))) # 填充else:token = token[:pad_size] # 截断seq_len = pad_size # 更新实际长度为pad_size# 将词转换为索引,未知词用UNK的索引words_line = []for word in token:words_line.append(vocab.get(word, vocab.get(UNK))) # 双重保险取UNK索引# 生成n-gram特征(FastText模型需要)buckets = config.n_gram_vocab # 从配置获取哈希桶数量bigram = []trigram = []for i in range(pad_size):# 为每个位置生成bigram和trigram的哈希值bigram.append(biGramHash(words_line, i, buckets))trigram.append(triGramHash(words_line, i, buckets))# 添加处理后的数据:词索引、标签、长度、bigram、trigramcontents.append((words_line, int(label), seq_len, bigram, trigram))return contents # 返回结构:[([...], 0, 32, [...], [...]), ...]# 加载并处理所有数据集train = load_dataset(config.train_path, config.pad_size)dev = load_dataset(config.dev_path, config.pad_size)test = load_dataset(config.test_path, config.pad_size)return vocab, train, dev, test
字/词分割
if use_word:tokenizer = lambda x: x.split(' ') # 按空格分割(词级别)else:tokenizer = lambda x: [y for y in x] # 按字符分割
首先定义分词器,如果是按照单词分割,就按照空格做分割;如果是按照字分割,就按照字符做分割。代码里的lambda表达式可以换成常规的写法:
if use_word:def tokenizer(x):return x.split(' ') # 按空格分割成词列表
else:def tokenizer(x):return [y for y in x] # 拆分成字符列表
加载/创建词汇表
接下来我们需要得到一个词汇表,它的作用就是把文字转换成数字ID。这里可以加载现成的,也可以自己生成一个词汇表。
# 加载或创建词汇表if os.path.exists(config.vocab_path):vocab = pkl.load(open(config.vocab_path, 'rb')) # 从文件加载else:vocab = build_vocab(config.train_path, tokenizer, MAX_VOCAB_SIZE, min_freq=1)pkl.dump(vocab, open(config.vocab_path, 'wb')) # 保存词汇表print(f"Vocab size: {len(vocab)}")
构建词汇表的第一步是计算各个文字出现的频次。
vocab_dic[word] = vocab_dic.get(word, 0) + 1
我们遍历每一行文本内容,将每一句文本的回车符去掉,并按照之前设计好的分词器进行分割。
遍历之后可以得到一个字典,字典里面记录的是每个字出现的次数。
这行代码的作用是从词汇字典中筛选出符合最小词频要求的单词,并按词频从高到低排序,最后截取前 max_size个单词形成最终的词汇列表。
vocab_list = sorted([_ for _ in vocab_dic.items() if _[1] >= min_freq], key=lambda x: x[1], reverse=True)[:max_size]
-
vocab_dic.items()
获取字典中的键值对列表,格式为[(单词1, 词频1), (单词2, 词频2), ...]
。
示例输入:{'apple':5, 'banana':3, 'cherry':7} → [('apple',5), ('banana',3), ('cherry',7)]
-
列表推导式筛选
if _ >= min_freq
过滤出词频≥min_freq 的单词,_
表示元组的第二个元素(词频)。如果min_freq=4, 筛选后:[('apple',5), ('cherry',7)]
(banana
因词频3被剔除) -
按词频降序排序
sorted(..., key=lambda x: x, reverse=True)
key=lambda x: x
:指定按元组的第二个元素(词频)排序。reverse=True
:降序排列(从高到低)。
排序后:[('cherry',7), ('apple',5)]
-
截取前
max_size
个元素[:max_size]
保留排序后的前max_size
个高频词。
若max_size=1
:
→ 结果:[('cherry',7)]
-
最终输出
vocab_list
得到处理后的词汇列表,格式为[(单词, 词频), ...]
,按词频降序排列且长度≤max_size
。
得到上图所示的文字频次表后,再做一步处理。下面这行代码的核心作用是将排序后的词汇列表转换为 {字: 索引}
的字典映射。
vocab_dic = {word_count[0]: idx for idx, word_count in enumerate(vocab_list)}
这行代码我看着也头大,可以拆解成下面这种写法:
vocab_dic = {}
for idx, word_count in enumerate(vocab_list):word = word_count # word_count 是 (单词, 词频) 元组,取第一个元素即单词vocab_dic[word] = idx
上面的代码看起来就清晰多了,先是将原先的数组转换为元组,这样每个字都被赋予了一个数字ID,再将{字:数字ID}的形式存到哈希表里,如下图所示:
# 添加未知词和填充符的索引(排在最后两位)vocab_dic.update({UNK: len(vocab_dic), PAD: len(vocab_dic) + 1})
最后再把未知词和填充符号添加到字典的末尾就好了。
数据集处理
with open(path, 'r', encoding='UTF-8') as f:for line in tqdm(f): # 显示进度条lin = line.strip()if not lin:continuecontent, label = lin.split('\t') # 分割文本和标签# 分词并处理长度token = tokenizer(content)seq_len = len(token)# 填充或截断至固定长度pad_sizeif pad_size:if len(token) < pad_size:# 注意:这里用vocab.get(PAD)可能存在错误,PAD应为词汇表中已存在的键token.extend([vocab.get(PAD)] * (pad_size - len(token))) # 填充else:token = token[:pad_size] # 截断seq_len = pad_size # 更新实际长度为pad_size# 将词转换为索引,未知词用UNK的索引words_line = []for word in token:words_line.append(vocab.get(word, vocab.get(UNK))) # 双重保险取UNK索引# 生成n-gram特征(FastText模型需要)buckets = config.n_gram_vocab # 从配置获取哈希桶数量bigram = []trigram = []for i in range(pad_size):# 为每个位置生成bigram和trigram的哈希值bigram.append(biGramHash(words_line, i, buckets))trigram.append(triGramHash(words_line, i, buckets))# 添加处理后的数据:词索引、标签、长度、bigram、trigramcontents.append((words_line, int(label), seq_len, bigram, trigram))
在训练的时候我们要保证每条语句的长度是一致的,所以要设置一个固定长度pad_size。小于这个长度就在句子后面增加PAD符号,大于这个长度就做后向截断。
# 生成n-gram特征(FastText模型需要)buckets = config.n_gram_vocab # 从配置获取哈希桶数量bigram = []trigram = []for i in range(pad_size):# 为每个位置生成bigram和trigram的哈希值bigram.append(biGramHash(words_line, i, buckets))trigram.append(triGramHash(words_line, i, buckets))
这段代码通过生成 Bigram(二元组) 和 Trigram(三元组) 的哈希特征,为深度学习模型提供局部词序信息,增强模型对短语和上下文关系的捕捉能力,尤其在处理短文本时效果显著。
- Bigram:相邻两个词的组合(如"深度学习" → "深度"-"学习")
- Trigram:相邻三个词的组合(如"自然语言处理" → "自然"-"语言"-"处理")
在实际的实现里我们做了哈希映射,原因是直接存储所有可能的N-Gram会导致特征维度爆炸。哈希映射的原理是将任意长度的N-Gram映射到固定范围的桶,做维度压缩。
def biGramHash(sequence, t, buckets):"""计算第t个位置的bigram哈希值公式: (前一个词的哈希值 * 质数) % 桶大小说明:如果t-1越界,用0代替(相当于用PAD的哈希值)"""t1 = sequence[t - 1] if t - 1 >= 0 else 0return (t1 * 14918087) % buckets # 14918087是一个大质数def triGramHash(sequence, t, buckets):"""计算第t个位置的trigram哈希值公式: (前两个词的哈希值组合 * 质数) % 桶大小说明:如果t-1或t-2越界,用0代替"""t1 = sequence[t - 1] if t - 1 >= 0 else 0t2 = sequence[t - 2] if t - 2 >= 0 else 0return (t2 * 14918087 * 18408749 + t1 * 14918087) % buckets # 双质数减少冲突
使用大质数组合相乘的设计,主要目的是通过数学特性降低哈希冲突率,同时保证计算效率。
质数的特点是只能被1和自身整除,在乘法运算中不同质数组合能生成唯一性更高的中间值;另外,使用两个间距大的千万级大质数,能够避免相邻词索引的小幅变化导致哈希值相似。比如:
词t-2=100, 词t-1=101 → 100*18,408,749 + 101*14,918,087 ≈ 3.3e9
词t-2=101, 词t-1=100 → 101*18,408,749 + 100*14,918,087 ≈ 3.4e9
相关文章:
基于LSTM的文本分类2——文本数据处理
前言 由于计算机无法认识到文字内容,因此在训练模型时需要将文字映射到计算机能够识别的编码内容。 映射的流程如下: 首先将文字内容按照词表映射到成唯一的数字ID。比如“我爱中国”,将“中”映射为1,将“国”映射到2。再将文…...
神经网络与深度学习:案例与实践——第三章(2)
神经网络与深度学习:案例与实践——第三章(2) 基于Softmax回归的多分类任务 Logistic回归可以有效地解决二分类问题,但在分类任务中,还有一类多分类问题,即类别数 C大于2 的分类问题。Softmax回归就是Log…...
Maven/Gradle的讲解
一、为什么需要构建工具? 在理解 Maven/Gradle 之前,先明确它们解决的问题: 依赖管理:项目中可能需要引入第三方库(如 Spring、JUnit 等),手动下载和管理这些库的版本非常麻烦。标…...
常见的HR面问题汇总
⚠️注意:以下仅是个人对问题的参考,具体情况视个人情况而定~ 1. 你觉得你有哪些优点和缺点? 优点:学习能力强,遇到问题会主动思考和查找解决方案;有责任心,对待工作认真负责&#…...
把握数据治理关键,释放企业数据潜能
数据治理是对数据资产管理行使权力和控制的活动集合,以下是关于它的详细介绍: 一、定义 数据治理是指从使用零散数据变为使用统一主数据、从具有很少或没有组织和流程治理到企业范围内的综合数据治理、从尝试处理主数据混乱状况到主数据井井有条的一个…...
优化 Web 性能:处理非合成动画(Non-Composited Animations)
在 Web 开发中,动画能够增强用户体验,但低效的动画实现可能导致性能问题。Google 的 Lighthouse 工具在性能审计中特别关注“非合成动画”(Non-Composited Animations),指出这些动画可能增加主线程负担,影响…...
房地产之后:探寻可持续扩张的产业与 GDP 新思
在经济发展的长河中,房地产长期占据着支柱产业的重要地位。其之所以能担当此重任,根源在于它深度嵌入了人们的生活与经济体系。住房,作为人类最基本的需求之一,具有不可替代的刚性。与其他现买按需生产的产业不同,房地产有着独特的消费逻辑。人们为了拥有一个稳定的居住之…...
Chapter02_数字图像处理基础
文章目录 图像的表示⭐模拟图像→数字图像均匀采样和量化均匀采样均匀量化 非均匀采样和量化 数字图像的表示二值图像灰度图像彩色图像 ⭐空间分辨率和灰度分辨率空间分辨率灰度分辨率 ⭐图像视觉效果影响因素采样数变化对图像视觉效果的影响空间分辨率变化对图像视觉效果的影响…...
【Android】UI开发:XML布局与Jetpack Compose的全面对比指南
随着Google推出Jetpack Compose这一现代化工具,我们面临一个关键选择:继续使用传统的XML布局,还是转向Compose? 一、语法对比:两种不同的构建方式 1. XML布局:基于标签的静态结构 XML通过嵌套标签定义UI元…...
浙大:LLM具身推理引擎Embodied-Reasoner
📖标题:Embodied-Reasoner: Synergizing Visual Search, Reasoning, and Action for Embodied Interactive Tasks 🌐来源:arXiv, 2503.21696 🌟摘要 🔸深度思维模型的最新进展已经证明了数学和编码任务的…...
form+ffmpeg+opus录音压缩音频
说明: formffmpegopus录音压缩音频 效果图: step1:opus格式录音 C:\Users\wangrusheng\RiderProjects\WinFormsApp11\WinFormsApp11\Form1.cs using System; using System.Diagnostics; using System.IO; using System.Windows.Forms;namespace WinFo…...
win10 笔记本电脑安装 pytorch+cuda+gpu 大模型开发环境过程记录
win10 笔记本电脑安装 pytorchcudagpu 大模型开发环境过程记录 文章部分内容参考 deepseek。 以下使用命令行工具 mingw64。 安装 Anaconda 安装位置: /c/DEVPACK/Anaconda3 然后安装 Python 3.10.16 (略) $ conda create -n pytorch_…...
LeetCode 2442:统计反转后的不同整数数量
目录 核心思想:数字的“拆分”与“重组” 分步拆解(以输入 123 为例) 关键操作详解 为什么能处理中间或末尾的0? 数学本质 总结 题目描述 解题思路 代码实现 代码解析 复杂度分析 示例演示 总结 核心思想:…...
获取inode的完整路径包含挂载的路径
一、背景 在之前的博客 缺页异常导致的iowait打印出相关文件的绝对路径-CSDN博客 里的 2.2.3 一节和 关于inode,dentry结合软链接及硬链接的实验-CSDN博客 里,我们讲到了在内核里通过inode获取inode对应的绝对路径的方法。对于根目录下的文件而言&#…...
解决上传PDF、视频、音频等格式文件到FTP站点时报错“将文件复制到FTP服务器时发生错误。请检查是否有权限将文件放到该服务器上”问题
一、问题描述 可以将文本文件(.txt格式),图像文件(.jpg、.png等格式)上传到我们的FTP服务器上;但是上传一些PDF文件、视频等文件时就会报错“ 将文件复制到FTP服务器时发生错误。请检查是否有权限将文件放到该服务器上。 详细信息: 200 Type set to l. 227 Entering Pas…...
git push
在 git push 命令中,分支名称的顺序和含义非常重要。其基本格式如下: git push <remote> <local_branch>:<remote_branch>各部分解释 <remote>:远程仓库的名称(如 origin)。<local_branc…...
Java中的四大引用类型详解
Java中的四大引用类型详解:强引用、软引用、弱引用、虚引用 1. 引用类型概览 Java提供了四种不同强度的引用类型,用于控制对象的生命周期和垃圾回收行为: 引用类型回收时机典型应用场景是否影响GC强引用永不回收(除非断开引用&…...
MySQL慢查询日志通俗指南
🍀 前言 如果你发现自己新写或者重写的接口查询速度变慢,你怎么定位原因呢?可以用explain分析我们的SQL原生代码,又或者可以考虑使用MySQL慢查询日志。这篇文章主要讲述什么是慢查询日志以及开发中可能用到的场景。 但是&#x…...
Kafka 如何保证消息可靠性?
Kafka 保证消息可靠性主要通过以下几个机制来实现,从生产者到消费者的整个链路上都设计了相应的保障措施: 1. 生产者(Producer)端的可靠性 ✅ a. acks 参数(确认机制) acks0:生产者不等待任何…...
【嵌入式系统设计师】知识点:第2章 嵌入式系统硬件基础知识
提示:“软考通关秘籍” 专栏围绕软考展开,全面涵盖了如嵌入式系统设计师、数据库系统工程师、信息系统管理工程师等多个软考方向的知识点。从计算机体系结构、存储系统等基础知识,到程序语言概述、算法、数据库技术(包括关系数据库、非关系型数据库、SQL 语言、数据仓库等)…...
C++重载运算符的本质
C 中运算符重载的本质就是函数调用,编译器会将运算符表达式转换为对特定函数的直接调用。以下是具体原理和实现细节: 1. 运算符重载的底层实现 当重载一个运算符(如 、、<<)时,实际上是在定义一个特殊名称的函数…...
Python解决“数字插入”问题
Python解决“数字插入”问题 问题描述测试样例解题思路代码 问题描述 小U手中有两个数字 a 和 b。第一个数字是一个任意的正整数,而第二个数字是一个非负整数。她的任务是将第二个数字 b 插入到第一个数字 a 的某个位置,以形成一个最大的可能数字。 你…...
深入讲解:智能合约中的读写方法
前言 在探秘区块链开发:智能合约在 DApp 中的地位及与传统开发差异一文中我提到对于智能合约中所有的写入其实都算是交易。而在一个完整的智能合约代码中最大的两个组成部分就是读取和写入。 本文将为你深入探讨该两者方法之间的区别。 写方法 写方法其实就是对区块链这一…...
Java进阶之旅-day05:网络编程
引言 在当今数字化的时代,网络编程在软件开发中扮演着至关重要的角色。Java 作为一门广泛应用的编程语言,提供了强大的网络编程能力。今天,我们深入学习了 Java 网络编程的基础知识,包括基本的通信架构、网络编程三要素、IP 地址、…...
Eliet Chat开发日志:信令服务器注册与通信过程
目录 1. 架构设计:信令服务器与客户端 2. 选择技术栈 3. 实现信令服务器 4. 客户端实现 5. 测试 6. 下一步计划 日期:2025年4月5日 今天的工作重点是实现两个设备通过信令服务器注册并请求对方公网地址信息,以便能够进行点对点通信。我…...
如何设计一个本地缓存
想获取更多高质量的Java技术文章?欢迎访问Java技术小馆官网,持续更新优质内容,助力技术成长 Java技术小馆官网https://www.yuque.com/jtostring 如何设计一个本地缓存 随着系统的复杂性和数据量的增加,如何快速响应用户请求、减…...
2024版idea使用Lombok时报找不到符号
今天在springboot项目中使用Lombok的Builder注解,启动时居然报了找不到符号的错,如下图 于是开始了漫长的寻找之路,首先去settings->Plugins中看自己的Lombok插件是否启动,发现确实是如此,然后看网上的教程去加上这…...
[Android安卓移动计算]:新建项目和配置环境步骤
文章目录 一:AndroidStudio 创建项目1. New Project2. 选择:Empty Activity 二:配置和下载SDK点击SDK 配置按钮选择API32和Android 9.0(Pie)再点击Apply点击接受条款声明进行安装 安装完后点击NEXT和OK出现:…...
$R^n$平面约束下的向量列
原向量: x → \overset{\rightarrow}{x} x→ 与 x → \overset{\rightarrow}{x} x→法向相同的法向量(与 x → \overset{\rightarrow}{x} x→同向) ( x → ⋅ n → ∣ n → ∣ 2 ) n → (\frac{\overset{\rightarrow}x\cdot\overset{\righta…...
混合编程的架构
在混合使用QML和Qt Widgets的环境中,是否必须严格遵循分层架构需要根据项目规模和复杂度来决定。以下是具体的决策指南和实施建议: 一、分层架构的适用性分析 #mermaid-svg-61Mlp9MrpFOoZPAO {font-family:"trebuchet ms",verdana,arial,sans…...
联网汽车陷入网络安全危机
有人能够入侵并控制汽车这一事实本身就令人恐惧,电影中的场景变成了现实。再加上汽车中的软件会处理和存储我们的个人数据,这种恐惧达到了一个新的高度。 一旦发生安全漏洞,我们的驾驶数据、联系人、通话记录、消息甚至位置信息等信息都可能…...
基于Spark的招聘数据预测分析推荐系统
【Spark】基于Spark的招聘数据预测分析推荐系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统能够高效处理海量招聘数据,利用Spark的强大计算能力实现快速分析和预测。该系…...
基于Spark的酒店数据分析系统
【Spark】基于Spark的酒店数据分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 本项目基于Python语言开发,借助Django进行后台框架的开发,搭建大数据虚拟机集群…...
网络安全L2TP实验
在FW1上,将接口g1/0/0添加进去trust区域 [USG6000V1]firewall zone trust [USG6000V1-zone-trust]add interface GigabitEthernet 1/0/0 在放通安全策略 在FW2上配置ip [USG6000V1]int g1/0/1 [USG6000V1-GigabitEthernet1/0/1]ip address 20.1.1.1 24 [USG6000…...
18.1.go连接redis
开发调试 Tiny RDM:跨平台GUI工具windows版本下载 https://download.csdn.net/download/chxii/90562932 支持多种格式查看:内置高级文本代码编辑器,支持语法高亮/代码折叠/错误提示 便捷搜索过滤:使用正则匹配搜索键后,仍可进行二级过滤,组合筛选数据更方便 调试分析…...
innodb如何实现mvcc的
InnoDB 实现 MVCC(多版本并发控制)的机制主要依赖于 Undo Log(回滚日志)、Read View(读视图) 和 隐藏的事务字段。以下是具体实现步骤和原理: 1. 核心数据结构 InnoDB 的每一行数据(…...
递归实现组合型枚举(DFS)
从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。 输入格式 两个整数 n,m,在同一行用空格隔开。 输出格式 按照从小到大的顺序输出所有方案,每行 1 个。 首先,同一行内的数升序排列,相邻两个数用一个空格隔开。…...
【Java学习日记18】:三元运算符和运算符的优先级
一、三元运算符 2. 示例代码 例题1: 例题2: 二、运算符优先级 1. 优先级规则 Java 运算符的执行顺序由优先级决定,优先级高的先执行。 只要记住小括号优先级最大即可,以后就用这玩意 四、总结 运算符优先级:小括号 () 是最高优先级工具...
基于springboot放松音乐在线播放系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 本放松音乐在线播放系统采用B/S架构,数据库是MySQL,网站的搭建与开发采用了先进的Java进行编写,使用了Spring Boot框架。该系统从两个对象:由管理员和用户来对系统进行设计构建。前台主要功能包括:用户注册、登录…...
【Scratch编程系列】Scratch编程软件界面
Scratch是一款由麻省理工学院(MIT) 设计开发的少儿编程工具。其特点是:使用者可以不认识英文单词,也可以不使用键盘,就可以进行编程。构成程序的命令和参数通过积木形状的模块来实现。用鼠标拖动指令模块到脚本区就可以了。 这个软…...
技术驱动革新,强力巨彩LED软模组助力创意显示
随着LED显示技术的不断突破,LED软模组因其独特的柔性特质和个性化显示效果,正逐渐成为各类应用场景的新宠。强力巨彩软模组R3.0H系列具备独特的可塑造型能力与技术创新,为商业展示、数字艺术、建筑装饰等领域开辟全新视觉表达空间。 LED…...
历年跨链合约恶意交易详解(三)——Nomad Bridge20220801
漏洞合约函数 /*** notice Given formatted message, attempts to dispatch* message payload to end recipient.* dev Recipient must implement a handle method (refer to IMessageRecipient.sol)* Reverts if formatted messages destination domain is not the Replicas d…...
Tensorflow、Pytorch与Python、CUDA版本的对应关系(更新时间:2025年4月)
更新时间:20250405 一、Tensorflow与Python 、CUDA版本对应关系 注意:从 TF 2.11 开始,Windows 不支持 CUDA 构建。要在 Windows 上使用 TensorFlow GPU,您需要在 WSL2 中构建/安装 TensorFlow 或将 tensorflow-cpu 与 TensorFlow-DirectML-Plugin 一起使用 1.1、CPU版本…...
FPGA实现按键切换流水灯不同亮灭模式
本文是一位fpga新手学习fpga的博客,写出这个shi山代码花了3个小时左右,途中学习了按键消抖、状态机等知识... 实现目标:通过按键控制led灯亮的不同模式,将每种模式用状态机表达。 代码如下: module led(input btn1,in…...
SeaTunnel系列之:部署SeaTunnel的Spark和Flink引擎
SeaTunnel系列之:部署SeaTunnel的Spark和Flink引擎 运行 SeaTunnel快速开始使用 Flink快速开始使用 Spark 运行 SeaTunnel 现在您已经下载了 SeaTunnel 二进制包和连接器插件。接下来,您可以选择不同的引擎选项来运行同步任务。 如果您使用 Flink 运行…...
“Pseudo Global Warming”:伪全球变暖PGW
“Pseudo Global Warming”:伪全球变暖PGW PGW方法概述🔍 一、PGW 方法的定义🧠 二、PGW 方法的基本原理🛠️ 三、PGW 方法的主要步骤📈 四、PGW 模拟时常涉及的变量📊 五、PGW 方法的优/缺点📚…...
【Linux学习笔记】初识进程概念和进程PCB
【Linux学习笔记】初识冯诺依曼体系和进程PCB 🔥个人主页:大白的编程日记 🔥专栏:Linux学习笔记 文章目录 【Linux学习笔记】初识冯诺依曼体系和进程PCB前言一. 冯诺依曼体系结构1.1 关于冯诺依曼体系的要点: 二. 操…...
deepseek回答ollama 下载模型的命令
ollama 下载模型的命令 已深度思考(用时 20 秒) 好的,我需要回答用户关于使用Ollama下载模型的命令的问题。首先,我需要回忆Ollama的基本用法。Ollama是一个用于本地运行大型语言模型的工具,支持多种模型如Llama、Mi…...
Kafka 中的生产者分区策略
Kafka 中的 生产者分区策略 是决定消息如何分配到不同分区的机制。这个策略对 Kafka 的性能、负载均衡、消息顺序性等有重要影响。了解它对于高效地使用 Kafka 进行消息生产和消费至关重要。 让我们一起来看 Kafka 中 生产者的分区策略,它如何工作,以及…...
Kafka 如何调优?
Kafka 的调优是个非常实用又容易踩坑的话题,涉及 Producer、Broker、Consumer、硬件、操作系统 等多个层面。我们可以从整体架构角度出发,按模块逐个给你讲清楚 实战建议。 🎯 Kafka 调优主要目标: 提高吞吐量降低延迟保证可靠…...