thefour--Love is like a tide

最后一部分了,要开始进行我们的训练了。

先上代码:

import os
import numpy as np
from tqdm import tqdm
import tensorflow as tf
from thetwo import NeuralStyleTransferModel
import theone
import thethree
#创建模型
model=NeuralStyleTransferModel()
#加载内容图片
content_image= thethree.load_images(theone.CONTENT_IMAGE_PATH)
#风格图片
style_image= thethree.load_images(theone.STYLE_IMAGE_PATH)
#计算出内容图片的内容特征备用
target_content_features = model([content_image,])['content']
target_style_features = model([style_image, ])['style']
M= theone.WIDTH * theone.HEIGHT
N=3
def _compute_content_loss(noise_features,target_features):"""计算指定层上两个特征之间的内容loss:param noise_features: 噪声图片在指定层的特征:param target_features: 内容图片在指定层的特征:return:"""content_loss=tf.reduce_sum(tf.square(noise_features - target_features))#计算系数x=2.*M*Ncontent_loss=content_loss/xreturn content_loss
def compute_content_loss(noise_content_features):"""计算当前图片的内容loss:param noise_content_features:噪声图片的内容特征:return:"""#初始化内容损失content_losses=[]#加权计算内容损失for(noise_feature,factor),(target_feature,_) in zip(noise_content_features,target_content_features):layer_content_loss=_compute_content_loss(noise_feature,target_feature)content_losses.append(layer_content_loss*factor)return tf.reduce_sum(content_losses)
def gram_matrix(feature):"""计算给定特征的格拉姆矩阵:param feature::return:"""#先交换维度,把channel维度提到最前面x=tf.transpose(feature,perm=[2,0,1])#reshape,压缩为2dx=tf.reshape(x,(x.shape[0],-1))#计算x和x的逆的乘积return x@tf.transpose(x)
def _compute_style_loss(noise_feature,target_feature):"""计算指定层上的两个特征之间的风格损失:param noise_feature: 噪声图片在指定层的特征:param target_feature: 风格图片在指定层的特征:return:"""noise_gram_matrix=gram_matrix(noise_feature)style_gram_matrix=gram_matrix(target_feature)style_loss=tf.reduce_sum(tf.square(noise_gram_matrix-style_gram_matrix))x=4.*(M**2)*(N**2)return style_loss/x
def compute_style_loss(noise_style_features):"""计算并返回图片的风格loss:param noise_style_features:噪声图片的风格特征:return:"""style_losses=[]for(noise_feature,factor),(target_feature,_) in zip(noise_style_features,target_style_features):layer_style_loss = _compute_style_loss(noise_feature,target_feature)style_losses.append(layer_style_loss*factor)return tf.reduce_sum(style_losses)
def total_loss(noise_features):"""计算总损失:param noise_features: 噪声图片特恒数据:return:"""content_loss=compute_content_loss(noise_features['content'])style_loss=compute_style_loss(noise_features['style'])return content_loss* theone.CONTENT_LOSS_FACTOR+style_loss* theone.STYLE_LOSS_FACTOR
optimizer=tf.keras.optimizers.Adam(theone.LEARNING_RATE)
noise_image=tf.Variable((content_image + np.random.uniform(-0.2, 0.2, (1, theone.HEIGHT, theone.WIDTH, 3))) / 2)
@tf.function
def train_one_step():"""一次迭代过程:return:"""
#求losswith tf.GradientTape() as tape:noise_outputs = model(noise_image)loss = total_loss(noise_outputs)#求梯度grad = tape.gradient(loss,noise_image)#梯度下降,更新噪声图片optimizer.apply_gradients([(grad,noise_image)])return loss
#创建保存生成图片的文件夹
if not os.path.exists(theone.OUTPUT_DIR):os.mkdir(theone.OUTPUT_DIR)
#共训练theone.EPOCHS个epoch
for epoch in range(theone.EPOCHS):#使用tqdm提示训练进度with tqdm(total=theone.STEPS_PER_EPOCH, desc='EPOCH{}/{}'.format(epoch + 1, theone.EPOCHS)) as pbar:#每个epoch训练theone.STEPS_PER_EPOCH次for step in range(theone.STEPS_PER_EPOCH):_loss=train_one_step()pbar.set_postfix({'loss':'%.4f'%float(_loss)})pbar.update(1)#每个epoch保存一次图片thethree.save_image(noise_image, '{}/{}.jpg'.format(theone.OUTPUT_DIR, epoch + 1))

下面我们来分析代码:

model=NeuralStyleTransferModel()
#加载内容图片
content_image= thethree.load_images(theone.CONTENT_IMAGE_PATH)
#风格图片
style_image= thethree.load_images(theone.STYLE_IMAGE_PATH)
#计算出内容图片的内容特征备用
target_content_features = model([content_image,])['content']
target_style_features = model([style_image, ])['style']
M= theone.WIDTH * theone.HEIGHT
N=3

先看第一部分,也可以说是准备工作。

model=NeuralStyleTransferModel()

首先,我们创建了一个神经风格迁移的实例对象,也就是我们之前定义的类模型,负责处理风格迁移的相关工作,主要是加载与训练集。

content_image= thethree.load_images(theone.CONTENT_IMAGE_PATH)

接下来,我们加载我们的数据,也就是图像,这里我们使用的是我们自定义的函数,参数是图片路径。这个函数(我们自定义的加载图片函数)包含了对图片进行处理等操作。

style_image= thethree.load_images(theone.STYLE_IMAGE_PATH)

同时,我们也要加载风格图片。

target_content_features = model([content_image,])['content']

将内容图片的变量作为参数传递给模型,调用call函数进行处理,返回的是字典,其中包含两个键值对,键分别是content和style。而在代码中我们指定的是content,即输出处理后得到的不同类型的特征。这些内容特征通常会在后序的神经风格迁移中用到,用于调整生成图片的内容以匹配院士图片的内容。

同理,

target_style_features = model([style_image, ])['style']

我们也提取了风格图片的特征。

M= theone.WIDTH * theone.HEIGHT
N=3

M和N用于后序的计算中,N是通道数。

def _compute_content_loss(noise_features,target_features):"""计算指定层上两个特征之间的内容loss:param noise_features: 噪声图片在指定层的特征:param target_features: 内容图片在指定层的特征:return:"""content_loss=tf.reduce_sum(tf.square(noise_features - target_features))#计算系数x=2.*M*Ncontent_loss=content_loss/xreturn content_loss

作用是计算指定层上两个特征之间的内容损失,接受的参数就是指定层上的特征,(噪声图片和内容图片)。

content_loss=tf.reduce_sum(tf.square(noise_features - target_features))

这段代码是计算内容损失的核心部分。参数noise_features和target_features是通过某种深度学习模型(很明显是卷积神经网络)在指定层次上提取的特征表示。

tf.square:计算了噪声图片特征与目标图片特征之间的差值,并将差值的每个元素取平方,然后通过td.reduce_sum()函数来进行求和,作为损失。

x = 2.0 * M * N 
content_loss = content_loss / x

这段代码是计算一个归一化系数即尺寸*通道数,再*2是为了增加一个权重因子,以调整损失的量级,使其更适合当前的优化目标。这种调整可以根据具体的任务和数据集来确定,以便更好的平衡损失的大小与优化过程的效率。

通常情况下,损失函数的值越小越好,因为我们的优化目标通常是将损失最小化,但是有时候,如果损失值过小,可能会导致数值不稳定或者优化过程变得过于敏感,通过乘以一个适当的权重因子,可以调整损失的量级,使其更适合当前的优化过程。
content_loss = content_loss / x这一计算时将损失除以归一化系数,使其不受图像尺寸和通道数的影响,从而更好的用于神经风格迁移的优化过程。

def compute_content_loss(noise_content_features):"""计算当前图片的内容loss:param noise_content_features:噪声图片的内容特征:return:"""#初始化内容损失content_losses=[]#加权计算内容损失for(noise_feature,factor),(target_feature,_) in zip(noise_content_features,target_content_features):layer_content_loss=_compute_content_loss(noise_feature,target_feature)content_losses.append(layer_content_loss*factor)return tf.reduce_sum(content_losses)

for (noise_feature,factor),(target_feature,_)in zip(noise_content_features,target_content_features):
迭代噪声图片的内容特征和目标图片的内容特征,这两个特征在创建模型的call函数里面就已经论述过提取方法了。

layer_content_loss=_compute_content_loss(noise_feature,target_feature)
content_losses.append(layer_content_loss*factor)

调用函数来计算其中的损失,并通过和权重进行相乘,添加到内容损失列表中。权重因子是用来调整每个层级对总体内容损失的贡献度,通过内容损失加权,更好控制了每个层级对整体损失的影响程度。

return tf.reduce_sum(content_losses)

我们将列表中所有元素进行求和,得到一个标量值,表示了当前图片的总的内容损失。

def gram_matrix(feature):"""计算给定特征的格拉姆矩阵:param feature::return:"""#先交换维度,把channel维度提到最前面x=tf.transpose(feature,perm=[2,0,1])#reshape,压缩为2dx=tf.reshape(x,(x.shape[0],-1))#计算x和x的逆的乘积return x@tf.transpose(x)

这段代码用于计算给定特征的格拉姆矩阵,参数是一个表示特征的张量。

x=tf.transpose(feature,perm=[2,0,1])这一行将特征张量的维度进行重新排列,将通道维度移动到最前面,参数perm=[2,0,1]表示将原始特征张量的通道维度(索引为2)放到最前面,其余维度保持不变。

x=tf.reshape(x,(x.shape[0],-1))

操作是将x张量进行重塑,x.shape[0]是取出张量x在第一个维度上的大小,通常表现的是批处理的大小。

这段代码的作用是将张量x重新塑造为一个二维张量,其中,第一个维度大小保持不变,即与原来张量的第一个维度大小相同(通常是指批处理的大小),而第二个维度则被设置为-1,这使得TensorFlow能够自动计算出第二个维度的大小。即剩下所有元素拉伸为单一的维度。这种操作通常用于准备特征张量以进行后续的矩阵运算或神经网络层的输入。

最后

return x@tf.transpose(x)

这段代码使用transpose函数来转置张量x,然后执行矩阵乘法操作。(两个维度是默认交换行和列的位置)。

在Python3.5以及以上的版本中@符号表示的是矩阵乘法操作,所以这一部分表示张量x与它的转置相乘。结果得到的是格拉姆矩阵,在神经网络中,格拉姆矩阵(Gram Matrix)用于捕捉特征之间的相关性,它的每个元素表示了相应特征之间的内积,从而反映它们之间的相似度和共同激活程度。

def _compute_style_loss(noise_feature,target_feature):"""计算指定层上的两个特征之间的风格损失:param noise_feature: 噪声图片在指定层的特征:param target_feature: 风格图片在指定层的特征:return:"""noise_gram_matrix=gram_matrix(noise_feature)style_gram_matrix=gram_matrix(target_feature)style_loss=tf.reduce_sum(tf.square(noise_gram_matrix-style_gram_matrix))x=4.*(M**2)*(N**2)return style_loss/x

定义计算风格损失函数,接受的参数是噪声图片在指定层的特征和风格图片在指定层的特征。

noise_gram_matrix=gram_matrix(noise_feature)格拉姆矩阵是用来衡量特征之间相关性的重要工具,通常用于计算风格损失,在风格迁移中,格拉姆矩阵捕捉了特征之间的相关性和共同激活程度,从而反映了风格的特征。这段代码的意思是将给定特征张量表示转化为格拉姆矩阵的形式,计算特征之间的相关性。

同理,下一句是将风格图片的特征张量转化为格拉姆矩阵。

然后计算表示噪声图片的格拉姆矩阵和表示风格图片的格拉姆矩阵之间的平方差。

归一化系数x,这里很明显是计算内容损失系数的平方,这个系数的计算是基于格拉姆矩阵的形式,格拉姆矩阵的计算设计特征之间的内积,这意味着其值与特征的数量有关。很明显,获得的风格损失矩阵是相比内容损失矩阵的二维拉伸,所以用于归一的系数也要二维拉伸。

同样我们返回归一化后的结果。

def compute_style_loss(noise_style_features):"""计算并返回图片的风格loss:param noise_style_features:噪声图片的风格特征:return:"""style_losses=[]for(noise_feature,factor),(target_feature,_) in zip(noise_style_features,target_style_features):layer_style_loss = _compute_style_loss(noise_feature,target_feature)style_losses.append(layer_style_loss*factor)return tf.reduce_sum(style_losses)

进行各层的风格损失汇总,类似于内容损失。

def total_loss(noise_features):"""计算总损失:param noise_features: 噪声图片特恒数据:return:"""content_loss=compute_content_loss(noise_features['content'])style_loss=compute_style_loss(noise_features['style'])return content_loss* theone.CONTENT_LOSS_FACTOR+style_loss* theone.STYLE_LOSS_FACTOR

计算总损失,content_loss=compute_content_loss(noise_features['content'])调用compute-content_loss函数计算总损失。下句同理。

返回的是将内容损失和风格损失加权之后求和的结果。

optimizer=tf.keras.optimizers.Adam(theone.LEARNING_RATE)

开始定义优化器喽:使用TensorFlow创建一个Adam优化器对象,Adam是一种常用的优化算法,使用羽于训练神经网络和深度学习模型。

在TensorFlow中,通过tf.keras.optimizers模块可以访问各种优化器,Adam优化器是一种自适应学习率优化算法,它能够在训练过程中自动调整学习率。参数是优化器的学习率,学习率决定了每次参数更新时所应用的步长大小,这通常是神经网络模型中事先定义好的超参数。

noise_image=tf.Variable((content_image + np.random.uniform(-0.2, 0.2, (1, theone.HEIGHT, theone.WIDTH, 3))) / 2)

这段代码创建一个TensorFlow变量noise_image,将其随机初始化为一个噪声图像,该图像是基于图像content_image而生成的。

np.random.uniform(-0.2, 0.2, (1, theone.HEIGHT, theone.WIDTH, 3))是一个NumPy函数调用,用于生成一个指定范围内均匀分布的随机数组,其形状为(1,theone.HEIGHT,theone.WIDTH,3)。

(-0.2,0.2)指定了随机数的范围,在这个例子中,随机数是从范围-0.2(包含)到0.2(不包含)之间的。数组中的元素是在-0.2到0.2之间的均匀分布的随机数,这个数组通常用于图像的随机噪声,用于引入一些随机性以便于训练模型。

@tf.function
def train_one_step():"""一次迭代过程:return:"""
#求losswith tf.GradientTape() as tape:noise_outputs = model(noise_image)loss = total_loss(noise_outputs)#求梯度grad = tape.gradient(loss,noise_image)#梯度下降,更新噪声图片optimizer.apply_gradients([(grad,noise_image)])return loss

这是神经风格迁移算法中的一次迭代过程。

@tf.function

是TensorFlow中的装饰器,用于将Python函数转化为TensorFlow计算图的一部分,以便提高计算效率。被@tf.function修饰的函数被编译为TensorFlow图,以便进行更有效的计算。

with tf.GradientTape() as tape:

这是TensorFlow中用于计算梯度的上下文管理器。在这个上下文中的操作会被记录下来,以便后序计算梯度。tf.GrandientTape()创建了一个新的梯度带,梯度带的作用是跟踪TensorFlow操作执行过程中所涉及的所有可训练变量的操作,并且能根据这些操作计算相对于这些变量的梯度。

with语句用于创建一个上下文环境,在这个环境中,所有的操作都会被记录在梯度带tape中,当退出这个上下文环境时,梯度带tape将被释放,其中的操作记录也将被删除,这样可以节省内存并提到效率。

noise_outputs = model(noise_image)

是得到噪声图像的输出,类型为字典,其中包括内容输出和风格输出。

loss = total_loss(noise_outputs)

用来计算总损失。

grad = tape.gradient(loss,noise_image)
#梯度下降,更新噪声图片
optimizer.apply_gradients([(grad,noise_image)])
return loss
grad = tape.gradient(loss,noise_image)

这行代码计算损失loss相对于噪声图片noise_image的梯度,在上下文的梯度带中,我们使用tape记录了计算损失的过程,以便TensorFlow能够自动计算相对于noise_image的梯度。

optimizer.apply_gradients([(grad,noise_image)])

这是TensorFlow中用于应用梯度下降步骤的方法,优化器会根据计算得到的梯度来更新模型参数,以使损失函数尽可能小。[(grad,noise_image)]是一个由梯度-变量对构成的列表,每个对都是一个元组,其中包含了要应用的梯度和相应的变量。在这里,(grad,noise_image)表示要将梯度grad应用于变量noise_image上。当调用apply_gradients()方法时,优化器会使用梯度下降算法或者其他优化算法,根据给定的梯度更新相应变量的值。这个过程是优化模型参数的关键步骤,它使模型能够逐渐的优化损失函数,提高模型的性能和准确性。

return loss

最后返回计算得到的损失值,以便在训练过程中进行监控和记录。

if not os.path.exists(theone.OUTPUT_DIR):os.mkdir(theone.OUTPUT_DIR)
#共训练theone.EPOCHS个epoch
for epoch in range(theone.EPOCHS):#使用tqdm提示训练进度with tqdm(total=theone.STEPS_PER_EPOCH, desc='EPOCH{}/{}'.format(epoch + 1, theone.EPOCHS)) as pbar:#每个epoch训练theone.STEPS_PER_EPOCH次for step in range(theone.STEPS_PER_EPOCH):_loss=train_one_step()pbar.set_postfix({'loss':'%.4f'%float(_loss)})pbar.update(1)#每个epoch保存一次图片thethree.save_image(noise_image, '{}/{}.jpg'.format(theone.OUTPUT_DIR, epoch + 1))
if not os.path.exists(theone.OUTPUT_DIR):os.mkdir(theone.OUTPUT_DIR)

代码首先检测是否存在指定的输出目录,如果不存在该目录,则使用os.mkdir函数创建该目录,这个目录用于 保存训练过程中生成的图片。

with tqdm(total=theone.STEPS_PER_EPOCH, desc='EPOCH{}/{}'.format(epoch + 1, theone.EPOCHS)) as pbar:

tqdm()函数用于创建一个进度条,用于显示训练进度。total参数设置了进度条的总步数。

desc参数设置了进度条的描述,格式化字符串'EPOCH{}/{}'.format(epoch+1,theone.EPOCHS)显示当前EPOCH序号和epoch的数量。

for step in range(theone.STEPS_PER_EPOCH):_loss=train_one_step()pbar.set_postfix({'loss':'%.4f'%float(_loss)})pbar.update(1)

进一步观察内部循环:

_loss=train_one_step()

进行了一步模型参数的更新。

pbar.set_postfix()方法更新了进度条的后缀,在这里,将损失值_loss格式化为四位小数,并将其作为进度条后缀,以便在训练过程中实时监控损失值变化。

之后使用pbar.update(1)的方法更新进度条,表示完成了当前的训练步骤,进度条的进度增加1。

最后一步了:

thethree.save_image(noise_image, '{}/{}.jpg'.format(theone.OUTPUT_DIR, epoch + 1))

!!!

使用thethree.save_image来保存生成的图片,该函数中有对张量转化为图像的操作,第二个参数是文件名,文件的路径是创建的文件夹下面加上轮数。

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

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

相关文章

代码随想录训练营第31天 | 理论基础、LeetCode 455.分发饼干、

目录 理论基础 视频讲解:手把手带你学会操作链表 | 贪心算法理论基础!_哔哩哔哩_bilibili LeetCode 455.分发饼干 文章讲解:代码随想录(programmercarl.com) 视频讲解:贪心算法,你想先喂哪个小孩?| Le…

【GameFramework框架内置模块】7、事件(Event)

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 【GameFramework框架】系列教程目录: https://blog.csdn.net/q7…

【Vue】路由

📝个人主页:五敷有你 🔥系列专栏:Vue ⛺️稳中求进,晒太阳 目录 路由 单页应用程序 总结: VueRouter 核心步骤: 组件存放目录的问题 路由的封装 声明式导航 声明式导航 - 导航链…

Go语言必知必会100问题-11 使用选项模式

使用选项模式 在设计API时,可能会遇到一个问题:如何处理可选配置?有效的解决可选配置问题可以提高API的灵活性。本文通过一个具体示例说明处理可选配置的一些方法。该示例的要求是设计一个对外提供创建HTTP服务器的库函数。函数定义如下&…

服了,阿里云服务器和腾讯云服务器价格差不多怎么选择?

2024年阿里云服务器和腾讯云服务器价格战已经打响,阿里云服务器优惠61元一年起,腾讯云服务器62元一年,2核2G3M、2核4G、4核8G、8核16G、16核32G、16核64G等配置价格对比,阿腾云atengyun.com整理阿里云和腾讯云服务器详细配置价格表…

高级语言期末2011级B卷(计算机学院)

1.编写函数&#xff0c;实现按照如下公式计算的功能&#xff0c;其中n为自然数 #include <stdio.h>int fac(int n) {if(n0)return 1;elsereturn n*fac(n-1); }float fun(int n) {float flag;float sum0;for(int i0; i<n; i) {flagi/((i1)*fac(i2));sumflag;}return su…

重推请求之curl和fiddler

在实际的项目中会有出现问题&#xff0c;想重现的场景&#xff0c;比较重新调用一个服务&#xff0c;那么如何进行快速的重推请求呢&#xff0c;记录下来&#xff0c;方便备查。 主要有curl和fiddler两种方式&#xff0c;下面详细说。 方式一、curl 命令 curl 是一个利用URL规…

云上攻防-云服务篇弹性计算服务器云数据库实例元数据控制角色AK控制台接管

知识点: 1、云服务-弹性计算服务器-元数据&SSRF&AK 2、云服务-云数据库-外部连接&权限提升 章节点&#xff1a; 云场景攻防&#xff1a;公有云&#xff0c;私有云&#xff0c;混合云&#xff0c;虚拟化集群&#xff0c;云桌面等 云厂商攻防&#xff1a;阿里云&am…

租赁小程序|租赁系统|租赁软件开发带来高效运营

随着社会的不断发展和科技的不断进步&#xff0c;越来越多的企业开始关注设备租赁业务。设备租赁作为一种短期使用设备的方式&#xff0c;为企业提供了灵活和成本节约的优势。针对设备租赁业务的管理和提升企业竞争力的需求&#xff0c;很多企业选择定制开发设备租赁系统。本文…

js 面试 1判断变量是否是数组 2 检测数据类型方法

1 是否是数组 1) typeof 检测数据类型运算符 优点&#xff1a;使用简单 缺点&#xff1a;只能检测基本类型&#xff08;除null外&#xff09; console.log(typeof(10)) //Number console.log(typeof(false)) //boolean console.log(typeof(hello)) //string console.log(typeof…

vue使用gitshot生成gif

vue使用gitshot生成gif 问题背景 本文将介绍vue中使用gitshot生成gif。 问题分析 解决思路&#xff1a; 使用input组件上传一个视频&#xff0c;获取视频文件后用一个video组件进行播放&#xff0c;播放过程进行截图生成图片数组。 demo演示上传一个视频&#xff0c;然后生…

如何使用Docker部署IT-Tools并结合内网穿透实现公网访问本地工具箱服务

作为程序员&#xff0c;在日常工作中&#xff0c;需要借助一些工具来提高我们工作效率&#xff0c;IT-Tools是为开发人员度身打造的一套便捷在线工具。它提供全面功能&#xff0c;使开发者能以更高效方式完成任务。经由IT-Tools&#xff0c;开发人员能轻松应对各类技术挑战&…

C++之数组

1&#xff0c;概述 所谓数组&#xff0c;就是一个集合&#xff0c;里面存放了相同类型的数据元素 特点1&#xff1a;数组中没干过数据元素都是相同的数据类型 特点2&#xff1a;数组都是连续存放位置组成的 2&#xff0c;一维数组 2.1 一维数组的定义 一维数组定义有三种…

Leetcode583. 两个字符串的删除操作 -代码随想录

题目&#xff1a; 代码(首刷自解 2024年2月29日&#xff09;&#xff1a; class Solution { public:// 动态规划 好像和找最长公共子序列一样&#xff1f;int minDistance(string word1, string word2) {int sz1 word1.size();int sz2 word2.size();// dp initvector<vec…

SD-WAN技术:优化国内外服务器访问的关键

在全球化的商业环境中&#xff0c;企业经常需要在国内访问国外的服务器。然而&#xff0c;由于地理位置和网络架构的限制&#xff0c;这种跨国访问往往会遇到速度慢、延迟高等问题。SD-WAN&#xff08;软件定义广域网&#xff09;技术的兴起&#xff0c;为企业提供了一种新的解…

八股文打卡day24——数据库(1)

面试题&#xff1a;左连接和右连接的区别&#xff1f; 我的回答&#xff1a; 左连接的SQL语句是&#xff1a;左表 left join 右表 on 连接条件&#xff0c;表示以左表为基础&#xff0c;将左表的的所有记录与右表进行连接。即使右表中没有与左表匹配的记录&#xff0c;左连接…

Linux-Uboot命令

help命令 进入 uboot 的命令行模式后输入“help”或者“&#xff1f;”&#xff0c;然后按下回车即可查看当前 uboot 所支持的命令。 查看某一个命令的帮助信息&#xff1a;&#xff1f;命令名称 或 help命令名称 信息查询命令 常用的和信息查询有关的命令有 3 个…

Cookie、Session和JWT

摘要&#xff1a;Cookie、Session和JWT都不是什么新的技术了&#xff0c;最近用到了就比较和总结下。 我们知道http协议是无状态的&#xff0c;用户登录后如何验证和保存用户状态呢&#xff1f;下面来介绍 1. 使用Cookie和Session验证登录状态 session是保存在服务端的一种数…

STM32串口通信(发送与接收数据)

文章目录 前言一、介绍部分通信接口术语解释 串口通信简介硬件电路电平标准串口参数串口时序USART简介USART框图USRAT基本结构数据帧起始位检测波特率发生器CH340G 二、实例部分使用串口发送数据接线图代码实现重定向printf需要勾上Use MicroLIB中文不乱码方法 串口的发送与接收…

C++ 之LeetCode刷题记录(三十六)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 目标&#xff1a;执行用时击败90%以上使用 C 的用户。 16. 最接近的三数之和 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你…