Python----循环神经网络(Word2Vec的优化)
一、负采样
基本思想:
在训练过程中,对于每个正样本(中心词和真实上下文词组成的词对),随机采样少量(如5-20个)负样本(中心词与非上下文词组成的词对)。
模型通过区分正样本和负样本来更新词向量,而非计算整个词汇表的概率分布。
实现方式:
将原始的多分类问题(预测所有可能的上下文词)转化为二分类问题,判断词对是否属于真实上下文。
例如,对于正样本(“猫”, “跳”),模型应输出1;对于负样本(“猫”, “苹果”),模型应输出0。
采样策略:
负样本通常根据词频进行采样,高频词更可能被选为负样本。
为平衡常见词和罕见词,采用词频的3/4次方进行平滑(如“苹果”比“袋熊”更容易被采样)。
为什么需要负采样?
解决计算瓶颈:
传统Softmax需要计算词汇表中所有词的得分并归一化,时间复杂度为O(V)(V为词汇表大小)。当VV达到百万级时,计算代价极高。
负采样将计算复杂度降至O(K+1)(K为负样本数,通常远小于VV),显著提升训练速度。
提升模型效果:
通过强制模型区分少量有代表性的负样本(如高频词),词向量的语义区分能力更强。
相比原始的层次Softmax等优化方法,负采样更简单且效果更好,尤其适合大规模数据。
避免梯度稀疏性:
传统方法中,每次仅更新正样本对应的权重,导致大部分词向量缺乏有效更新。
负采样让更多词(正样本+负样本)参与参数更新,提升训练稳定性。
二、CBOW + 负采样(negative sampling)
2.1、词划分
2.2、过程
要确定模型对上下文预测的词究竟是真正的中心词还是负样本。
如果是真正的中心词,预测结果为1;如果是负样本,预测结果为0。
词向量的重要作用之 一是通过运算揭示词与词之间的关系或相似度。点积是衡量两个向量相似度的一种方 式,因此只需将预测结果与真正的中心词和负样本的词向量进行点积,再通过 softmax转换为概率,就可以得到0或1的预测结果,从而实现二分类。
总体来说,模型的输入包括三个部分:上下文、中心词和负样本。这三个输入根据字 典中的value作为索引,从嵌入矩阵中取得相应的词向量。然后,对于上下文的处 理,类似于原始的CBOW模型,进行平均操作,接着经过线性层。随后,分别将结果 与中心词和负样本的词向量进行点积,最终与实际的标签进行二分类交叉熵计算。将 这些损失加总,得到总体的损失。
import os
import random
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
import redef setup_seed(seed):np.random.seed(seed) # Numpy module.random.seed(seed) # Python random module.os.environ['PYTHONHASHSEED'] = str(seed)torch.manual_seed(seed)if torch.cuda.is_available():torch.cuda.manual_seed(seed)torch.cuda.manual_seed_all(seed)torch.backends.cudnn.benchmark = Falsetorch.backends.cudnn.deterministic = Truesetup_seed(0)
# 数据预处理
corpus = ["jack like dog", "jack like cat", "jack like animal","dog cat animal", "banana apple cat dog like", "dog fish milk like","dog cat animal like", "jack like apple", "apple like", "jack like banana","apple banana jack movie book music like", "cat dog hate", "cat dog like"
]# 将句子分词,并转换为小写
def tokenize(sentence):# \b 单词的边界# \w+ 匹配一个或者多个单词字符(字母,数字,下划线)# \[,.!?] 匹配逗号、句号、感叹号和问号word_list = []for word in re.findall(r"\b\w+\b|[,.!?]", sentence):word_list.append(word.lower())return word_listwords = []
for sentence in corpus:for word in tokenize(sentence):words.append(word)# print(words)
word_counts = Counter(words)vocab = sorted(word_counts, key=word_counts.get, reverse=True)
# print(vocab)# {"like": 1, "dog": 2}
# 创建词汇表到索引的隐射
vocab2int = {word: ii for ii, word in enumerate(vocab, 1)}
# print(vocab2int)
# 将所有的单词转换为索引表示
int2vocab = {ii: word for ii, word in enumerate(vocab, 1)}
# print(int2vocab)# 将所有单词变成索引
word2index = [vocab2int[word] for word in words]
# print(word2index)
window = 1
center = []
context = []
negative_samples = []
num_negative_samples = 2
vocab_size = len(vocab2int) + 1 # 词汇表大小for i, target in enumerate(word2index[window: -window], window):# print(i, target)# 数据:3 1 2 3 1 4# 索引:0 1 2 3 4 5center.append(target)con = word2index[i - window: i] + word2index[i + 1: i + 1 + window]context.append(word2index[i - window: i] + word2index[i + 1: i + 1 + window])# 生成负样本,确保不包括中心词和上下文negative_samples_i = []for _ in range(num_negative_samples):#2negative_sample = np.random.choice(range(1,vocab_size))while negative_sample == target or negative_sample in con:negative_sample = np.random.choice(range(1,vocab_size))negative_samples_i.append(negative_sample)negative_samples.append(negative_samples_i)# 特殊标记:0,用于填充或者标记未知单词
# <SOS>: 句子起始标识符
# <EOS>:句子结束标识符
# <PAD>:补全字符
# <MASK>:掩盖字符
# <SEP>:两个句子之间的分隔符
# <UNK>:低频或未出现在词表中的词
# vocab_size = len(vocab2int) + 1 # 词汇表大小
embedding_dim = 2 # 嵌入维度class CBOWModel(nn.Module):def __init__(self, vocab_size, embedding_dim):super(CBOWModel, self).__init__()# self.embeddings = nn.Parameter(torch.randn(vocab_size, embedding_dim)) # 初始化embedding矩阵self.embeddings = nn.Embedding(vocab_size, embedding_dim) # 初始化embedding矩阵print(self.embeddings)self.linear = nn.Linear(embedding_dim, vocab_size) # vocab_size = 14def forward(self, context):# context.shape -> [bs, 2]context_emb = self.embeddings(context) # 上下文的嵌入向量# print(context_emb.shape) # torch.Size([4, 2, 2])avg_emb = torch.mean(context_emb, dim=1, keepdim=True).squeeze(1)y = self.linear(avg_emb)return ymodel = CBOWModel(vocab_size, embedding_dim)
cri = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)bs = 4
epochs = 2000
for epoch in range(1, epochs + 1):total_loss = 0for batch_index in range(0, len(context), bs):# 上下文的Tensor # torch.Size([4, 2])context_tensor = torch.tensor(context[batch_index: batch_index + bs])# 中心词的tensor torch.Size([4, 1])center_tensor = torch.tensor(center[batch_index: batch_index + bs]).view(bs, 1)# 负样本的tensor torch.Size([4, 2])negative_tensor = torch.tensor(negative_samples[batch_index: batch_index + bs])# 正样本# 第一维 4 表示批次中的样本数。第二维 2 表示上下文窗口的大小,即每个中心词有两个上下文词。第三维 2 表示嵌入向量的维度(embedding dimension)。# print("context_emb", context_emb.shape) # torch.Size([4, 2, 2])context_emb = model.embeddings(context_tensor)# 第一维 4 表示批次中的样本数。第二维 2 表示嵌入向量的维度。# print("center_emb", center_emb.shape) # torch.Size([4, 2])center_emb = model.embeddings(center_tensor).squeeze(1)# 将多个上下文词的嵌入向量平均化,从而得到每个样本的平均上下文嵌入向量。这有助于简化计算,并能更好地表示上下文的总体信息。avg_context_emb = torch.mean(context_emb, dim=1) # avg_context_emb 的形状是 [4, 2]"""例如:context_emb = torch.tensor([[[0.1, 0.2], [0.3, 0.4]],[[0.5, 0.6], [0.7, 0.8]],[[0.9, 1.0], [1.1, 1.2]],[[1.3, 1.4], [1.5, 1.6]]])通过执行 avg_context_emb = torch.mean(context_emb, dim=1),我们在 context_window_size 维度上取平均值:对第一个样本:[(0.1 + 0.3)/2, (0.2 + 0.4)/2] = [0.2, 0.3]对第二个样本:[(0.5 + 0.7)/2, (0.6 + 0.8)/2] = [0.6, 0.7]对第三个样本:[(0.9 + 1.1)/2, (1.0 + 1.2)/2] = [1.0, 1.1]对第四个样本:[(1.3 + 1.5)/2, (1.4 + 1.6)/2] = [1.4, 1.5]avg_context_emb = torch.tensor([[0.2, 0.3],[0.6, 0.7],[1.0, 1.1],[1.4, 1.5]])avg_context_emb 的形状变为 [4, 2],2表示每个样本的平均上下文嵌入向量。"""positive_scores = torch.matmul(avg_context_emb, center_emb.t())# 第一维 4 批次中的样本数(batch size)。第二维 4 与批次中的每一个中心词的相似性分数。# positive_scores[i, j] 表示第 i 个样本的平均上下文嵌入与第 j 个样本的中心词嵌入之间的相似性分数。# print("positive_scores", positive_scores.shape) # torch.Size([4, 4])# positive_labels = torch.ones_like(positive_scores)positive_labels = torch.eye(bs) # 生成对角线为1的标签矩阵positive_loss = cri(positive_scores, positive_labels)# 负样本# negative_emb.shape [batch_size, num_negative_samples, embedding_dim]:[4, 2, 2]negative_emb = model.embeddings(negative_tensor)# avg_context_emb->[batch_size, embedding_dim] avg_context_emb.unsqueeze(1)->[batch_size, 1, embedding_dim]# negative_emb.permute(0, 2, 1) -> [batch_size, embedding_dim, num_negative_samples]# torch.matmul() -> [batch_size, 1, num_negative_samples]# 通过 squeeze(1) 去掉维度1,得到 negative_scores 的形状是 [batch_size, num_negative_samples]。# 每个中心词与其负样本(negative)之间的相似性negative_scores = torch.matmul(avg_context_emb.unsqueeze(1), negative_emb.permute(0, 2, 1)).squeeze(1)negative_labels = torch.zeros_like(negative_scores)negative_loss = cri(negative_scores, negative_labels)loss = positive_loss + negative_losstotal_loss += lossoptimizer.zero_grad()loss.backward()optimizer.step()avg_loss = total_loss / len(context)if epoch == 1 or epoch % 50 == 0:print(f"Epoch [{epoch}/{epochs}] Loss {avg_loss:.4f}")# 每个单词的embedding向量word_vec = model.embeddings.weight.detach().numpy()# print(word_vec)x = word_vec[:, 0]y = word_vec[:, 1]selected_word = ["dog", "cat", "milk", "like", "animal", "fish", "banana", "apple"]selected_word_index = [vocab2int[word] for word in selected_word]selected_word_x = x[selected_word_index]selected_word_y = y[selected_word_index]plt.cla()plt.scatter(selected_word_x, selected_word_y, color="blue")# 将每个点的标注加上for word, x, y in zip(selected_word, selected_word_x, selected_word_y):plt.annotate(word, (x, y), textcoords="offset points", xytext=(0, 10))plt.pause(0.5)plt.show()
三、Skip-gram + 负采样(negative sampling)
3.1、词划分
3.2、过程
要确定模型对中心词预测的词究竟是真正的上下文还是负样本。
如果是真正的上下文,预测结果为1;如果是负样本,预测结果为0。
点积是衡量两个向量相似度的一种方式,因此只需将预测结果与真正的上下文和负样 本的词向量进行点积,再通过softmax转换为概率,就可以得到0或1的预测结果,从 而实现二分类。
所以,模型的输入包括三个部分:上下文、中心词和负样本。这三个输入根据字典中 的value作为索引,从嵌入矩阵中取得相应的词向量。然后,对于中心词的处理,类 似于原始的Skip-gram模型,进行复制操作,接着经过线性层。随后,分别将结果与 上下文和负样本的词向量进行点积,最终与实际的标签进行二分类交叉熵计算。
最后将这些损失加总,得到总体的损失。
import torch
import torch.nn as nn
import torch.optim as optimimport matplotlib.pyplot as plt
import numpy as np
from collections import Counter
from scipy.spatial.distance import cosine
import re# 数据预处理
corpus = ["jack like dog", "jack like cat", "jack like animal","dog cat animal", "banana apple cat dog like", "dog fish milk like","dog cat animal like", "jack like apple", "apple like", "jack like banana","apple banana jack movie book music like", "cat dog hate", "cat dog like"
]# 将句子分词,并转换为小写
def tokenize(sentence):word_list = []for word in re.findall(r"\b\w+\b|[,.!?]", sentence):word_list.append(word.lower())return word_listwords = []
for sentence in corpus:for word in tokenize(sentence):words.append(word)word_counts = Counter(words)vocab = sorted(word_counts, key=word_counts.get, reverse=True)# 创建词汇表到索引的隐射
vocab2int = {word: ii for ii, word in enumerate(vocab, 1)}
int2vocab = {ii: word for ii, word in enumerate(vocab, 1)}# 将所有单词变成索引
word2index = [vocab2int[word] for word in words]
window = 1
center = []
context = []
negative_samples = []
num_negative_samples = 2
vocab_size = len(vocab2int) + 1 # 词汇表大小for i, target in enumerate(word2index[window: -window], window):center.append(target)con = word2index[i - window: i] + word2index[i + 1: i + 1 + window]context.append(word2index[i - window: i] + word2index[i + 1: i + 1 + window])# 生成负样本,确保不包括中心词和上下文negative_samples_i = []for _ in range(num_negative_samples):negative_sample = np.random.choice(range(1,vocab_size))while negative_sample == target or negative_sample in con:negative_sample = np.random.choice(range(1,vocab_size))negative_samples_i.append(negative_sample)negative_samples.append(negative_samples_i)torch.manual_seed(10)embedding_dim = 2 # 嵌入维度class SkipGramModel(nn.Module):def __init__(self, vocab_size, embedding_dim):super(SkipGramModel, self).__init__()self.embeddings = nn.Embedding(vocab_size, embedding_dim)self.linear = nn.Linear(embedding_dim, vocab_size)def forward(self, center):center_emb = self.embeddings(center)y = self.linear(center_emb)return ymodel = SkipGramModel(vocab_size, embedding_dim)
cri = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)bs = 4
epochs = 2000
for epoch in range(1, epochs + 1):total_loss = 0for batch_index in range(0, len(center), bs):center_tensor = torch.tensor(center[batch_index: batch_index + bs]).view(bs, 1)context_tensor = torch.tensor(context[batch_index: batch_index + bs])negative_tensor = torch.tensor(negative_samples[batch_index: batch_index + bs])# 在 CBOW 模型中,计算的是平均上下文嵌入与中心词嵌入之间的相似性,得到的 positive_scores 的形状是 [batch_size, batch_size]。# 在 Skip-gram 模型中,计算的是每个中心词与其上下文词之间的相似性,得到的 positive_scores 的形状是 [batch_size, num_context_samples]。# 正样本# 第一维 4 表示批次中的样本数。第二维 2 表示上下文窗口的大小,即每个中心词有两个上下文词。第三维 2 表示嵌入向量的维度(embedding dimension)。# print("context_emb", context_emb.shape) # torch.Size([4, 2, 2])context_emb = model.embeddings(context_tensor)# 第一维 4 表示批次中的样本数。第二维 2 表示嵌入向量的维度。# print("center_emb", center_emb.shape) # torch.Size([4, 2])center_emb = model.embeddings(center_tensor).squeeze(1)# center_emb->[batch_size, embedding_dim] center_emb.unsqueeze(1)->[batch_size, 1, embedding_dim]# context_emb.permute(0, 2, 1) -> [batch_size, embedding_dim, num_context_samples]# torch.matmul() -> [batch_size, 1, num_context_samples]# 通过 squeeze(1) 去掉维度1,得到 negative_scores 的形状是 [batch_size, num_context_samples]positive_scores = torch.matmul(center_emb.unsqueeze(1), context_emb.permute(0, 2, 1)).squeeze(1)print("positive_scores:", positive_scores.shape)positive_labels = torch.ones_like(positive_scores)positive_loss = cri(positive_scores, positive_labels)# 负样本# negative_emb.shape [batch_size, num_negative_samples, embedding_dim]:[4, 2, 2]negative_emb = model.embeddings(negative_tensor)# center_emb->[batch_size, embedding_dim] center_emb.unsqueeze(1)->[batch_size, 1, embedding_dim]# negative_emb.permute(0, 2, 1) -> [batch_size, embedding_dim, num_negative_samples]# torch.matmul() -> [batch_size, 1, num_negative_samples]# 通过 squeeze(1) 去掉维度1,得到 negative_scores 的形状是 [batch_size, num_negative_samples]negative_scores = torch.matmul(center_emb.unsqueeze(1), negative_emb.permute(0, 2, 1)).squeeze(1)negative_labels = torch.zeros_like(negative_scores)negative_loss = cri(negative_scores, negative_labels)loss = positive_loss + negative_losstotal_loss += lossoptimizer.zero_grad()loss.backward()optimizer.step()avg_loss = total_loss / len(center)if epoch == 1 or epoch % 50 == 0:print(f"Epoch [{epoch}/{epochs}] Loss {avg_loss:.4f}")# 每个单词的embedding向量word_vec = model.embeddings.weight.detach().numpy()# print(word_vec)x = word_vec[:, 0]y = word_vec[:, 1]selected_word = ["dog", "cat", "milk", "like", "animal", "fish", "banana", "apple"]selected_word_index = [vocab2int[word] for word in selected_word]selected_word_x = x[selected_word_index]selected_word_y = y[selected_word_index]plt.cla()plt.scatter(selected_word_x, selected_word_y, color="blue")# 将每个点的标注加上for word, x, y in zip(selected_word, selected_word_x, selected_word_y):plt.annotate(word, (x, y), textcoords="offset points", xytext=(0, 10))plt.pause(0.5)plt.show()
四、函数解释
self.embeddings = nn.Embedding(vocab_size, embedding_dim) 和 self.embeddings = nn.Parameter(torch.randn(vocab_size, embedding_dim)) 是实现词嵌入的两种不同方式。它们的主要区别在于功能和用法。
4.1、nn.Embedding
self.embeddings = nn.Embedding(vocab_size, embedding_dim)
功能 | 是 PyTorch 中专门用来做嵌入查找 (embedding lookup)的层。它接受整数索引并返回相应的嵌入向 量。 |
特性 | 自动处理输入的索引值并查找相应的嵌入向量 支持 padding 索引,用于处理变长序列 提供了权重初始化选项 |
代码简洁 | 可以简化代码,因为它封装了嵌入查找 的逻辑。 |
import torch
from torch import nn
class CBOWModel(nn.Module):def __init__(self, vocab_size, embedding_dim):super(CBOWModel, self).__init__()self.embeddings = nn.Embedding(vocab_size, embedding_dim)self.linear = nn.Linear(embedding_dim, vocab_size)def forward(self, context):context_emb = self.embeddings(context)avg_emb = torch.mean(context_emb, dim=1, keepdim=True).squeeze(1)y = self.linear(avg_emb)return y
4.2、nn.Parameter
self.embeddings = nn.Parameter(torch.randn(vocab_size,embedding_dim))
功能 | 是一个张量,它被视为模块的一部分,可以被训 练。它只是一个可训练的张量,没有任何额外的功能。 |
特性 | 需要手动实现嵌入查找的逻辑。 更加灵活,可以完全自定义嵌入查找的过程。 适合需要对嵌入层进行更多自定义操作的情况。 |
import torch
from torch import nn
class CBOWModel(nn.Module):def __init__(self, vocab_size, embedding_dim):super(CBOWModel, self).__init__()self.embeddings = nn.Parameter(torch.randn(vocab_size, embedding_dim))self.linear = nn.Linear(embedding_dim, vocab_size)def forward(self, context):context_emb = self.embeddings(context)avg_emb = torch.mean(context_emb, dim=1, keepdim=True).squeeze(1)y = self.linear(avg_emb)return y
4.3、使用上的区别
嵌入查找
nn.Embedding 自动处理输入索引并查找嵌入向量。
使用 nn.Parameter 时,需要手动实现索引查找。
功能
nn.Embedding 提供了很多方便的功能,比如处理 padding 索 引、权重初始化等。
nn.Parameter 只是一个可训练的张量,需要手动实现所有功能。
相关文章:

Python----循环神经网络(Word2Vec的优化)
一、负采样 基本思想: 在训练过程中,对于每个正样本(中心词和真实上下文词组成的词对),随机采样少量(如5-20个)负样本(中心词与非上下文词组成的词对)。 模型通过区分正…...

Simon J.D. Prince《Understanding Deep Learning》
学习神经网络和深度学习推荐这本书,这本书站位非常高,且很多问题都深入剖析了,甩其他同类书籍几条街。 多数书,不深度分析、没有知识体系,知识点零散、章节之间孤立。还有一些人Tian所谓的权威,醒醒吧。 …...

开搞:第四个微信小程序:图上县志
原因:我换了一个微信号来搞,因为用同一个用户,备案只能一个个的来。这样不行。所以我换了一个。原来注册过小程序。现在修改即可。注意做好计划后,速度备案和审核,不然你时间浪费不起。30元花起。 结构: -…...
模型评估与调优(PyTorch)
文章目录 模型评估方法混淆矩阵混淆矩阵中的指标ROC曲线(受试者工作特征)AUCR平方残差均方误差(MSE)均方根误差(RMSE)平均绝对误差(MAE) 模型调优方法交叉验证(CV&#x…...
sockaddr结构体详解
在网络编程中,sockaddr 结构体用于表示套接字的地址信息。由于不同协议(如 IPv4、IPv6、Unix 域套接字)的地址格式不同,实际使用中通常通过以下三种变体结构来处理不同类型的地址: 1. 通用地址结构:struct …...

Seata源码—7.Seata TCC模式的事务处理一
大纲 1.Seata TCC分布式事务案例配置 2.Seata TCC案例服务提供者启动分析 3.TwoPhaseBusinessAction注解扫描源码 4.Seata TCC案例分布式事务入口分析 5.TCC核心注解扫描与代理创建入口源码 6.TCC动态代理拦截器TccActionInterceptor 7.Action拦截处理器ActionIntercept…...

【语法】C++的map/set
目录 平衡二叉搜索树 set insert() find() erase() swap() map insert() 迭代器 erase() operator[] multiset和multimap 在之前学习的STL中,string,vector,list,deque,array都是序列式容器,它们的…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Live View Kit (3)
1.问题描述: 通过Push Kit创建实况窗之后,再更新实况窗失败,平台查询提示“实况窗端更新失败,通知未创建或已经过期”。 解决方案: 通过Push Kit更新实况窗内容的过程是自动更新的。客户端在创建本地实况窗后&#…...

vue vite textarea标签按下Shift+Enter 换行输入,只按Enter则提交的实现思路
注意input标签不能实现,需要用textarea标签 直接看代码 <template><textareav-model"message"keydown.enter"handleEnter"placeholder"ShiftEnter 换行,Enter 提交"></textarea> </template>&l…...
MySQL多线程备份工具mysqlpump详解!
MySQLPUMP备份工具详解 1. 概述 MySQLPump 是 MySQL 5.7 引入的一个客户端备份工具,用于替代传统的 mysqldump 工具。它提供了并行处理、进度状态显示、更好的压缩支持等新特性,能够更高效地执行 MySQL 数据库备份操作。 2. 主要特性 并行处理&#x…...
创建信任所有证书的HttpClient:Java 实现 HTTPS 接口调用,等效于curl -k
在 Java 生态中,HttpClient 和 Feign 都是调用第三方接口的常用工具,但它们的定位、设计理念和使用场景有显著差异。以下是详细对比: DIFF1. 定位与抽象层级 特性HttpClientFeign层级底层 HTTP 客户端库(处理原始请求/响应&#…...
Redisson分布式集合原理及应用
Redisson是一个用于Redis的Java客户端,它简化了复杂的数据结构和分布式服务的使用。 适用场景对比 数据结构适用场景优点RList消息队列、任务队列、历史记录分布式共享、阻塞操作、分页查询RMap缓存、配置中心、键值关联数据支持键值对、分布式事务、TTLRSet去重集…...

深入理解 PlaNet(Deep Planning Network):基于python从零实现
引言:基于模型的强化学习与潜在动态 基于模型的强化学习(Model-based Reinforcement Learning)旨在通过学习环境动态的模型来提高样本效率。这个模型可以用来进行规划,让智能体在不需要与真实环境进行每一次决策交互的情况下&…...
精益数据分析(75/126):用户反馈的科学解读与试验驱动迭代——Rally的双向验证方法论
精益数据分析(75/126):用户反馈的科学解读与试验驱动迭代——Rally的双向验证方法论 在创业的黏性阶段,用户反馈是优化产品的重要依据,但如何避免被表面反馈误导?如何将反馈转化为可落地的迭代策略&#x…...

仿腾讯会议——视频发送接收
1、 添加音频模块 2、刷新图片,触发重绘 3、 等比例缩放视频帧 4、 新建视频对象 5、在中介者内定义发送视频帧的函数 6、完成发送视频的函数 7、 完成开启/关闭视频 8、绑定视频的信号槽函数 9、 完成开启/关闭视频 10、 完成发送视频 11、 完成刷新图片显示 12、完…...

从3.7V/5V到7.4V,FP6291在应急供电智能门锁中的应用
在智能家居蓬勃发展的当下,智能门锁以其便捷、安全的特性,成为现代家庭安防的重要组成部分。在智能门锁电量耗尽的情况下,应急电源外接移动电源(USB5V输入) FP6291升压到7.4V供电可应急开锁。增强用户在锁具的安全性、…...
java后端-海外登录(谷歌/FaceBook)
前言 由于最近公司的项目要在海外运行,因此需要对接海外的登录,目前就是谷歌和facebook两种,后面支付也是需要的,后续再进行书写 谷歌登录 这个相对比较容易,而且只提供给安卓即可,废话就不多说了,直接贴解决方案 引入maven依赖 <dependency> <groupId>com.go…...

【人工智障生成日记1】从零开始训练本地小语言模型
🎯 从零开始训练本地小语言模型:MiniGPT TinyStories(4090Ti) 🧭 项目背景 本项目旨在以学习为目的,从头构建一个完整的本地语言模型训练管线。目标是: ✅ 不依赖外部云计算✅ 完全本地运行…...

Selenium-Java版(frame切换/窗口切换)
frame切换/窗口切换 前言 切换到frame 原因 解决 切换回原来的主html 切换到新的窗口 问题 解决 回到原窗口 法一 法二 示例 前言 参考教程:Python Selenium Web自动化 2024版 - 自动化测试 爬虫_哔哩哔哩_bilibili 上期文章:Sel…...

一文深度解析:Pump 与 PumpSwap 的协议机制与技术差异
在 Solana 链上,Pump.fun 和其延伸产品 PumpSwap 构成了 meme coin 发行与流通的两大核心场景。从初期的游戏化发行模型,到后续的自动迁移与交易市场,Pump 系列协议正在推动 meme coin 从“爆发性投机”走向“协议化运营”。本文将从底层逻辑…...

星云智控v1.0.0产品发布会圆满举行:以创新技术重构物联网监控新生态
星云智控v1.0.0产品发布会圆满举行:以创新技术重构物联网监控新生态 2024年5月15日,成都双流蛟龙社区党群服务中心迎来了一场备受业界瞩目的发布会——优雅草科技旗下”星云智控v1.0.0”物联网AI智控系统正式发布。本次发布会吸引了包括沃尔沃集团、新希…...

SpringBoot(一)--- Maven基础
目录 前言 一、初始Maven 1.依赖管理 2.项目构建 3.统一项目结构 二、IDEA集成Maven 1.Maven安装 2.创建Maven项目 2.1全局设置 2.2 创建SpringBoot项目 2.3 常见问题 三、单元测试 1.JUnit入门 2.断言 前言 Maven 是一款用于管理和构建Java项目的工具ÿ…...

基于FPGA控制电容阵列与最小反射算法的差分探头优化设计
在现代高速数字系统测试中,差分探头的信号完整性直接影响测量精度。传统探头存在阻抗失配导致的信号反射问题,本文提出一种通过FPGA动态控制电容阵列,结合最小反射算法的优化方案,可实时调整探头等效容抗,将信号反射损…...

kakfa 基本了解
部署结构 Kafka 使用zookeeper来协商和同步,但是kafka 从版本3.5正式开始deprecate zookeeper, 同时推荐使用自带的 kraft. 而从4.0 开始则不再支持 zookeeper。 所以 kafka 是有control plane 和 data plane 的。 data plane 就是broker,control plane…...
基于Browser Use + Playwright 实现AI Agent操作Web UI自动化
Browser Use是什么 Browser Use是一个开源项目官网:Browser Use - Enable AI to control your browser,专为大语言模型(LLM)设计的只能浏览器工具,能够让AI像人类一样自然的浏览和操作网页,支持多标签页管…...

Origin绘制多因子柱状点线图
多因子柱状点线图是一种结合柱状图和点线图的复合图表,常用于同时展示多个因子(变量)在不同分组下的分布和趋势变化。 适用场景: (1)比较多个因子在不同分组中的数值大小(柱状图)&a…...

Web漏洞扫描服务的特点与优势:守护数字时代的安全防线
在数字化浪潮中,Web应用程序的安全性已成为企业业务连续性和用户信任的核心要素。随着网络攻击手段的不断升级,Web漏洞扫描服务作为一种主动防御工具,逐渐成为企业安全体系的标配。本文将从特点与优势两方面,解析其价值与应用场景…...
iOS 直播技术及优化
iOS直播技术的实现和优化涉及多个技术环节,需结合协议选择、编解码方案、播放器技术及性能调优等多方面。 一、核心技术实现 协议选择与传输优化 HLS(HTTP Live Streaming):苹果官方推荐,基于HTTP分片传输,…...

抛弃传统P2P技术,EasyRTC音视频基于WebRTC打造教育/会议/远程巡检等场景实时通信解决方案
一、方案背景 随着网络通信发展,实时音视频需求激增。传统服务器中转方式延迟高、资源消耗大,WebP2P技术由此兴起。EasyRTC作为高性能实时通信平台,集成WebP2P技术,实现低延迟、高效率音视频通信,广泛应用于教育、医疗…...

俄罗斯军总参情报局APT28组织瞄准援乌后勤供应链发起全球网络攻击
2025年5月,由美国、英国、欧盟和北约网络安全与情报机构联合发布的最新网络安全公告披露,俄罗斯军总参情报局(GRU)第85特别服务中心第26165部队(又称APT28、Fancy Bear、Forest Blizzard和BlueDelta)正持续…...