karpathy makemore 3

1 Introduction

这一章,karpathy通过分析参数初始化对模型的收敛的影响作为一个引子,让我们看到神经网络这一个复杂的系统,对grad非常敏感,以及有一些技巧可以解决grad敏感的问题。

2 准备工作

2.1 dataloader

import torch
block_size = 3
def generate_datasets(words):
    X = []
    Y = []
    for w in words:
        context = [0] * block_size
        for char in w + '.':
            ix = stoi[char]
            X.append(context)
            Y.append(ix)
            context = context[1:] + [ix]
    Xb = torch.tensor(X)
    Yb = torch.tensor(Y)
    return Xb, Yb
import random
random.shuffle(words)
total_size = len(words)
n_train = int(0.8 * total_size)
n_val = int(0.9 * total_size)
Xtr, Ytr = generate_datasets(words[:n_train])
Xdev, Ydev = generate_datasets(words[n_train:n_val])
Xte, Yte = generate_datasets(words[n_val:])

2.2 embedding

我们要复现这个网络,这里embedding采用了一个Matrix C。并且在这个系列的第二部分,我们也看到了字符和Matrix中的映射关系。
从字符转换成字符索引

stoi = {char : i + 1 for i, char in enumerate(sorted(set(''.join(words))))}
stoi['.'] = 0
itos = {i : char for char, i in stoi.items()}
vocab_size = len(itos)
print(vocab_size)

从字符索引转换成embedding, 这里

n_batch = 32
n_emb = 10
ix = torch.randint(0, Xtr.shape[0], (n_batch,), generator=g)
Xb = Xtr[ix]
Xb
C = torch.randn(vocab_size, n_emb, generator=g)

emb = C[Xb]
print(emb.shape)

在这里插入图片描述

2.3 定义网络结构

初始化参数

n_hidden = 200
W1 = torch.randn(n_emb * block_size, n_hidden, generator=g)
b1 = torch.randn(n_hidden, generator=g)
W2 = torch.randn(n_hidden, vocab_size, generator=g)
b2 = torch.randn(vocab_size, generator=g)
parameters = [C, W1, b1, W2, b2]
for p in parameters:
    p.requires_grad = True

开始训练

import torch.nn.functional as F
max_iter = 200000
lossi = []
for i in range(max_iter):
    ix = torch.randint(0, Xtr.shape[0], (n_batch,), generator=g)
    Xb, Yb = Xtr[ix], Ytr[ix]
    emb = C[Xb]
    hprect = torch.tanh(emb.view(n_batch, -1) @ W1 + b1)
    logits = hprect @ W2 + b2
    loss = F.cross_entropy(logits, Yb)
    for p in parameters:
        p.grad = None
    loss.backward()
    lr = 0.1 if i < 100000 else 0.01
    with torch.no_grad():
        for p in parameters:
            p.data -= lr * p.grad.data
    lossi.append(loss.item())

    if i % 1000 == 0:
            print(f"Iteration: {i}/{max_iter}, Loss: {loss.item()}")

在这里插入图片描述
对比一下训练集和测试集的差距

@torch.no_grad()
def batch_infer(datasets):
    X, Y = {
        'train' : (Xtr, Ytr),
        'val' : (Xdev, Ydev),
        'test' : (Xte, Yte),
    }[datasets]
    emb = C[X]
    hprect = emb.view(-1, block_size * n_emb) @ W1 + b1
    logits = torch.tanh(hprect) @ W2 + b2
    loss = F.cross_entropy(logits, Y)
    print(f"{datasets} loss is {loss.item()}")

batch_infer('train')
batch_infer('val')

train loss is 2.1050491333007812
val loss is 2.1596949100494385

观察一下目前的网络输出的结果怎么样

for _ in range(20):
    context = [0] * block_size
    ch = []
    while(True):
        X = torch.tensor(context)
        emb = C[X]
        hprect = emb.view(-1, block_size * n_emb) @ W1 + b1
        logits = torch.tanh(hprect) @ W2 + b2
        probs = torch.softmax(logits, dim=-1).squeeze(0)
        ix = torch.multinomial(probs, num_samples=1).item()
        context = context[1:] + [ix]
        ch.append(itos[ix])
        if ix == 0:
            break
    print(''.join(ch))

输出的结果是:

dar.
charia.
arlonathana.
jana.
avietaviannee.
dex.
aarion.
laron.
westishawmyraddia.
julatamyae.
sharha.
gius.
daviyan.
laydi.
sha.
rudya.
hal.
masiel.
aari.
kelya.

3 参数初始化诊断

3.1 分析参数初始化的结果

  • step1: 理论分析初始的loss
    torch.tensor([1/28]).log()

tensor([3.3322])

目前的第一次迭代的loss是22.5远大于3.33
分析一下为什么会这样
主要看一下logits的结果是如何影响loss的

logits = torch.tensor([1., 1., 1., 1.])
log_softmax = F.softmax(logits, dim=0)
loss = -log_softmax[1].log()
print(loss.item())

1.3862943649291992

logits = torch.tensor([1., -2., 1., 2.])
log_softmax = F.softmax(logits, dim=0)
loss = -log_softmax[1].log()
print(loss.item())

4.561941146850586
也就是说logits如果有出现负数的情况,就很容易导致初始的误差非常大
这个分布非常的不好,太宽了

import matplotlib.pyplot as plt
# 绘制直方图
plt.figure(figsize=(8, 6))
plt.hist(logits.flatten().detach(), bins=50)
plt.title("Logits Distribution")
plt.xlabel("Value")
plt.ylabel("Frequency")
plt.show()

在这里插入图片描述
那么我们就可以将我们的W2和b2 修改一下
W2 = torch.randn(n_hidden, vocab_size, generator=g) * 0.1
b2 = torch.randn(vocab_size, generator=g) * 0

Iteration: 0/200000, Loss: 4.399700164794922

  • step2: 关注激活函数
    这里使用的是tanh作为激活函数,如果初始的时候,tanh的数值很大,那么很有可能grad就变得很小,后续即使迭代了,也没法更新参数。
    在这里插入图片描述
    方式1: 直接查看hprect的直方图以及输入的直方图
    在这里插入图片描述
    在这里插入图片描述
    可以看到是因为输入到tanh的数值分布太广了,至少还需要缩小0.1。
    我们还有一个工具,可以查看激活函数的输入输出对整体的batch_size的结果:

可以看到有一列完全都是白的,也就是kernel dead

import numpy as np
# 绘制灰度图
plt.figure(figsize=(8, 6))
plt.imshow(hprect.abs() > 0.99, cmap='gray', interpolation='nearest')
plt.show()

在这里插入图片描述
更正修改W1的参数到0.2
实际上来说我这个参数还是不太好
在这里插入图片描述

  • 理论分析
    随着网络深度的加深,方差会越来越大。
    在这里插入图片描述
    所以需要对参数要进行系数进行缩放。

3.2 手动进行缩放

kaiming的论文[1],

W1 = torch.randn(n_emb * block_size, n_hidden, generator=g) * (5/3) / (n_emb * block_size) ** 0.5
b1 = torch.randn(n_hidden, generator=g) * 0.01
W2 = torch.randn(n_hidden, vocab_size, generator=g) * 0.01
b2 = torch.randn(vocab_size, generator=g) * 0

这一次的loss分布是
在这里插入图片描述
已经分不太出来趋势了,需要处理一下,

avg_lossi = np.mean(np.array(lossi[:len(lossi)//1000*1000]).reshape(-1,1000), axis=1)
plt.figure(figsize=(8, 6))
plt.plot(avg_lossi)
plt.xlabel('Iterations (x1000)'), plt.ylabel('Average Loss'), plt.title('Training Loss')
plt.show()

在这里插入图片描述
在迭代100000万次的时候loss发生了突变,因为这个时候我们的学习率发生了较大的变化。

3.3 通过batchnorm

思想也很简单,在第一次进入activation的时候,我们希望对输入做一次标准化处理,防止数据过于集中,或者过于分散。

  • 用统计的方法计算均值和方差,然后再进入activation的时候去.
    但是这样的结果有一个问题,设计上来说我们只希望在初始的时候,进行batchnorm,后面每次迭代的时候并不希望还继续batchnorm。
n_hidden = 200
W1 = torch.randn(n_emb * block_size, n_hidden, generator=g) 
b1 = torch.randn(n_hidden, generator=g) * 0
W2 = torch.randn(n_hidden, vocab_size, generator=g) * 0.01
b2 = torch.randn(vocab_size, generator=g) * 0
bngain = torch.ones((1, n_hidden))
bnbias = torch.zeros((1, n_hidden))
bnmean_running = torch.zeros((1, n_hidden))
bnstd_running = torch.ones((1, n_hidden))
parameters = [C, W1, b1, W2, b2, bngain, bnbias]
for p in parameters:
    p.requires_grad = True
import torch.nn.functional as F
max_iter = 200000
lossi = []
for i in range(max_iter):
    ix = torch.randint(0, Xtr.shape[0], (n_batch,), generator=g)
    Xb, Yb = Xtr[ix], Ytr[ix]
    emb = C[Xb]
    hpreact = emb.view(n_batch, -1) @ W1 + b1
    bnmeani = hpreact.mean(0, keepdim=True)
    bnstdi = hpreact.std(0, keepdim=True)
    hpreact = bngain * (hpreact - bnmeani) / bnstdi + bnbias
    with torch.no_grad():
        bnmean_running = 0.999 * bnmean_running + 0.001 * bnmeani
        bnstd_running = 0.999 * bnstd_running + 0.001 * bnstdi
    h = torch.tanh(hpreact) 
    logits = h @ W2 + b2
    loss = F.cross_entropy(logits, Yb)
    
    for p in parameters:
        p.grad = None
    loss.backward()
    lr = 0.1 if i < 100000 else 0.01
    with torch.no_grad():
        for p in parameters:
            p.data -= lr * p.grad
    lossi.append(loss.item())

    if i % 1000 == 0:
            print(f"Iteration: {i}/{max_iter}, Loss: {loss.item()}")
    # break

这里比较麻烦的地方在于,(hp - hp_mean_running) / (hp_var_running + 1e-5),因为这里采用的是batchnorm,采用广播方式,对于每个batch 样本进行归一化处理。

h = gamma * ((hp - hp_mean_running) / (hp_var_running + 1e-5)) + beta

在这里插入图片描述
进行校验:

@torch.no_grad()
def batch_infer(datasets):
    X, Y = {
        'train' : (Xtr, Ytr),
        'val' : (Xdev, Ydev),
        'test' : (Xte, Yte),
    }[datasets]
    emb = C[X]
    hpreact = emb.view(emb.shape[0], -1) @ W1
    hpreact = bngain * (hpreact - bnmean_running) / bnstd_running + bnbias
    h = torch.tanh(hpreact) # (N, n_hidden)
    logits = h @ W2 + b2 # (N, vocab_size)
    loss = F.cross_entropy(logits, Y)
    print(f'{datasets}, loss is: {loss}')

batch_infer('train')
batch_infer('val')

这次的结果

train, loss is: 2.1178481578826904
val, loss is: 2.1550681591033936

4 使用torch的高级语法

我们现在的网络比较简单,在我们将网络扩展到更深的网络之前,我们需要将之前代码按照torch开发模式,进行转换

4.1定义一个最基础的网络

import torch

class Linear:
    def __init__(self, in_features, out_features, bias=True):
        self.weight = torch.randn(in_features, out_features, generator=g) / in_features ** 0.5
        self.bias = torch.zeros(out_features) if bias else None
        
    def __call__(self, x):
        self.out = x @ self.weight
        if self.bias is not None:
            self.out += self.bias
        return self.out
    
    def parameters(self):
        return [self.weight] + ([] if self.bias is None else [self.bias])

class Tanh:
    def __call__(self, x):
        self.out = torch.tanh(x)
        return self.out
    
    def parameters(self):
        return []

class BatchNorm1D:
    def __init__(self, n_layers, eps=1e-5, momentum = 0.1):
        self.gain = torch.ones(n_layers)
        self.bias = torch.zeros(n_layers)
        self.bn_mean_running = torch.zeros(n_layers)
        self.bn_std_running = torch.ones(n_layers)
        self.training = True
        self.momentum = momentum
        self.eps = eps
    
    def __call__(self, x):
        if self.training:
            bn_meani = x.mean(0, keepdim=True)
            bn_stdi = x.var(0, keepdim=True)

        else:
            bn_meani = self.bn_mean_running
            bn_stdi = self.bn_std_running

        self.out = self.gain * (x - bn_meani) / (bn_stdi + self.eps) + self.bias    
        if self.training:
            with torch.no_grad():
                self.bn_mean_running = (1 - self.momentum) * self.bn_mean_running + self.momentum* self.bn_meani
                self.bn_std_running = (1 - self.momentum) * self.bn_std_running + self.momentum * self.bn_stdi
        return self.out
    
    def parameters(self):
        return [self.gain, self.bias]

参数初始化

layers = [Linear(block_size*n_emb, n_hidden), Tanh(),
Linear(n_hidden, n_hidden), Tanh(),
Linear(n_hidden, n_hidden), Tanh(),
Linear(n_hidden, n_hidden), Tanh(),
Linear(n_hidden, vocab_size)
]
# 设置参数属性
C = torch.randn(vocab_size, n_emb, generator=g)
parameters = [C] + [p for layer in layers for p in layer.parameters()]
with torch.no_grad():
    # layers[-1].gamma *= 0.1
    layers[-1].weight *= 0.1
    for layer in layers[:-1]:
        if isinstance(layer, Linear):
            layer.weight *= 5/3

for p in parameters:
    p.requires_grad = True

进行网络训练

import torch.nn.functional as F

max_iter = 200000
lossi = []
for i in range(max_iter):
    ix = torch.randint(0, Xtr.shape[0], (n_batch,), generator=g)
    Xb, Yb = Xtr[ix], Ytr[ix]
    emb = C[Xb]
    x = emb.view(-1, block_size*n_emb)
    for layer in layers:
        x = layer(x)
    logits = x
    loss = F.cross_entropy(logits, Yb)
    for p in parameters:
        p.grad = None
    
    loss.backward()
    lr = 0.1 if i < 100000 else 0.01
    for p in parameters:
        p.data -= lr * p.grad
    
    lossi.append(loss.item())

    if i % 1000 == 0:
            print(f"Iteration: {i}/{max_iter}, Loss: {loss.item()}")
    break

接下来我们还是一样,希望看一下激活函数的输出分布

import matplotlib.pyplot as plt
# visualize histograms
plt.figure(figsize=(20, 4)) # width and height of the plot
legends = []
for i, layer in enumerate(layers[:-1]): # note: exclude the output layer
  if isinstance(layer, Tanh):
    t = layer.out
    print('layer %d (%10s): mean %+.2f, std %.2f, saturated: %.2f%%' % (i, layer.__class__.__name__, t.mean(), t.std(), (t.abs() > 0.97).float().mean()*100))
    hy, hx = torch.histogram(t, density=True)
    plt.plot(hx[:-1].detach(), hy.detach())
    legends.append(f'layer {i} ({layer.__class__.__name__}')
plt.legend(legends);
plt.title('activation distribution')

layer 1 ( Tanh): mean +0.01, std 0.71, saturated: 11.75%
layer 3 ( Tanh): mean -0.01, std 0.68, saturated: 7.56%
layer 5 ( Tanh): mean +0.02, std 0.67, saturated: 6.77%
layer 7 ( Tanh): mean +0.02, std 0.67, saturated: 7.23%
在这里插入图片描述

从图中可以按出来第一层还是出现很多激活函数的输出在边界,在来看一下grad的分布
这里需要注意,如果需要查看中间的grad结果,需要在正向传播的时候,retain_grad这样才能查看

    for layer in layers:
        layer.out.retain_grad() # AFTER_DEBUG: would take out retain_graph

整体的grad还是非常小的

layer 1 ( Tanh): mean -0.000008, std 2.809027e-04
layer 3 ( Tanh): mean +0.000006, std 2.608607e-04
layer 5 ( Tanh): mean -0.000004, std 2.412728e-04
layer 7 ( Tanh): mean -0.000004, std 2.213307e-04
在这里插入图片描述
整体看一下所有参数grad的分布,如果大量的分布在0附近,表示参数出现dead的情况很多

#整体看一下grad
# visualize histograms
plt.figure(figsize=(20, 4))  # width and height of the plot
legends = []
for i, p in enumerate(parameters):
    t = p.grad
    if p.ndim == 2:
        print('weight %10s | mean %+f | std %e | grad:data ratio %e' % (tuple(p.shape), t.mean(), t.std(), t.std() / p.std()))
        hy, hx = torch.histogram(t, density=True)
        plt.plot(hx[:-1].detach(), hy.detach())
        legends.append(f'{i} {tuple(p.shape)}')
plt.legend(legends)
plt.title('weights gradient distribution')

在这里插入图片描述
我们现在这个参数初始化还是不太好,需要加上batchNorm1D

g = torch.Generator().manual_seed(2147483647)
n_hidden = 100
n_emb = 10

layers = [
    Linear(block_size*n_emb, n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
    Linear(n_hidden,         n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
    Linear(n_hidden,         n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
    Linear(n_hidden,         n_hidden, bias=False), BatchNorm1d(n_hidden), Tanh(),
    Linear(n_hidden,       vocab_size, bias=False), BatchNorm1d(vocab_size),
]
# 设置参数属性
C = torch.randn(vocab_size, n_emb, generator=g)
parameters = [C] + [p for layer in layers for p in layer.parameters()]
with torch.no_grad():
    layers[-1].gamma *= 0.1
    # layers[-1].weight *= 0.1
    for layer in layers[:-1]:
        if isinstance(layer, Linear):
            layer.weight *= 1.0

for p in parameters:
    p.requires_grad = True
import torch.nn.functional as F

max_iter = 200000
lossi = []
for i in range(max_iter):
    ix = torch.randint(0, Xtr.shape[0], (n_batch,), generator=g)
    Xb, Yb = Xtr[ix], Ytr[ix]
    emb = C[Xb]
    x = emb.view(-1, block_size*n_emb)
    for layer in layers:
        x = layer(x)
    logits = x
    loss = F.cross_entropy(logits, Yb)
    for layer in layers:
        layer.out.retain_grad() # AFTER_DEBUG: would take out retain_graph
    for p in parameters:
        p.grad = None
    
    loss.backward()

    lr = 0.1 if i < 100000 else 0.01
    for p in parameters:
        p.data -= lr * p.grad
    
    lossi.append(loss.item())

    if i % 1000 == 0:
            print(f"Iteration: {i}/{max_iter}, Loss: {loss.item()}")
    break

激活函数的输出饱和程度好了很多

layer 2 ( Tanh): mean +0.01, std 0.63, saturated: 2.94%
layer 5 ( Tanh): mean -0.00, std 0.63, saturated: 3.25%
layer 8 ( Tanh): mean -0.01, std 0.64, saturated: 2.69%
layer 11 ( Tanh): mean -0.00, std 0.63, saturated: 3.03%
在这里插入图片描述
激活函数的grad
方差和标准差是一个量级的
layer 2 ( Tanh): mean +0.000000, std 7.608066e-04
layer 5 ( Tanh): mean +0.000000, std 6.368941e-04
layer 8 ( Tanh): mean -0.000000, std 5.829208e-04
layer 11 ( Tanh): mean +0.000000, std 4.975214e-04
在这里插入图片描述

4.2 如何查看学习的速度

查看grad和参数值的比值

import torch.nn.functional as F

max_iter = 200000
lossi = []
ud = []
for i in range(max_iter):
    ix = torch.randint(0, Xtr.shape[0], (n_batch,), generator=g)
    Xb, Yb = Xtr[ix], Ytr[ix]
    emb = C[Xb]
    x = emb.view(-1, block_size*n_emb)
    for layer in layers:
        x = layer(x)
    logits = x
    loss = F.cross_entropy(logits, Yb)
    for layer in layers:
        layer.out.retain_grad() # AFTER_DEBUG: would take out retain_graph
    for p in parameters:
        p.grad = None
    
    loss.backward()

    lr = 0.1 if i < 100000 else 0.01
    for p in parameters:
        p.data -= lr * p.grad
    
    lossi.append(loss.item())
    with torch.no_grad():
        ud.append([((-lr * p.grad).std() / p.data.std()).log10().item() for p in parameters])
    if i % 1000 == 0:
        print(f"Iteration: {i}/{max_iter}, Loss: {loss.item()}")
    # break

显示图像

plt.figure(figsize=(20, 4))
legends = []
for i,p in enumerate(parameters):
  if p.ndim == 2:
    plt.plot([ud[j][i] for j in range(len(ud))])
    legends.append('param %d' % i)
plt.plot([0, len(ud)], [-3, -3], 'k') # these ratios should be ~1e-3, indicate on

在这里插入图片描述

References

[1] Kaiming init" paper: https://arxiv.org/abs/1502.01852

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DS:顺序表、单链表的相关OJ题训练

欢迎各位来到 Harper.Lee 的学习小世界&#xff01; 博主主页传送门&#xff1a;Harper.Lee的博客主页 想要一起进步的uu可以来后台找我交流哦&#xff01; 在DS&#xff1a;单链表的实现 和 DS&#xff1a;顺序表的实现这两篇文章中&#xff0c;我详细介绍了顺序表和单链表的…

PAT (Advanced Level) - 1047 Student List for Course

模拟 #include <iostream> #include <cstring> #include <algorithm> #include <vector> using namespace std;const int N 40010, M 2510;int n, k; string name[N]; vector<int> list[M];int main() {scanf("%d%d", &n, &…

万万没想到,延缓帕金森病进展的“玄机”竟然就在腿上?【北京仁爱堂】

帕金森病患者的腿部变化&#xff0c;其实可以反应出很多问题。例如行走的变化问题、步态的异常等问题&#xff0c;可以反应病情轻重程度。而通过保持腿部肌肉活动的状态&#xff0c;也可以使帕金森病的症状得到一定的缓解&#xff0c;甚至有助于防止病情恶化。 帕金森病腿部变…

2024年五一数学建模竞赛C题论文首发

基于随机森林的煤矿深部开采冲击地压危险预测 摘要 煤炭作为中国重要的能源和工业原料&#xff0c;其开采活动对国家经济的稳定与发展起着至关重要的作用。本文将使用题目给出的数据探索更为高效的数据分析方法和更先进的监测设备&#xff0c;以提高预警系统的准确性和可靠性…

Photoshop前言

Photoshop前言 分辨率图像格式工具界面组件 分辨率 分辨率是指单位长度内包含的像素点的数量&#xff0c;其单位通常为像素/英寸&#xff08;ppi&#xff09;&#xff0c;300ppi表示每英寸包含300个像素点。对于1英寸1英寸大小的图像&#xff0c;若分辨率为72ppi&#xff0c;则…

基于Pytorch深度学习——多层感知机

本文章来源于对李沐动手深度学习代码以及原理的理解&#xff0c;并且由于李沐老师的代码能力很强&#xff0c;以及视频中讲解代码的部分较少&#xff0c;所以这里将代码进行尽量逐行详细解释 并且由于pytorch的语法有些小伙伴可能并不熟悉&#xff0c;所以我们会采用逐行解释小…

Visio 2021 (64bit)安装教程

Visio 2021 (64bit)安装教程 ​ 通知公告 Visio 2021 (64位) 是一款流程图和图表设计工具,主要用于创建和编辑各种类型的图表和图形。它提供了一个直观的界面和丰富的功能,可以帮助用户快速绘制专业的流程图、组织结构图、网络图、平面图、数据库模型等。 具体来说,Visio 20…

Go 语言数组

Go 语言提供了数组类型的数据结构。 数组是具有相同唯一类型的一组已编号且长度固定的数据项序列&#xff0c;这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。 相对于去声明 number0, number1, ..., number99 的变量&#xff0c;使用数组形式 numbers[0], num…

Spring Cloud——Circuit Breaker上篇

Spring Cloud——Circuit Breaker上篇 一、分布式系统面临的问题1.服务雪崩2.禁止服务雪崩故障 二、Circuit Breaker三、resilience4j——服务熔断和降级1.理论知识2.常用配置3.案例实战&#xff08;1&#xff09;COUNT_BASED&#xff08;计数的滑动窗口&#xff09;&#xff0…

Spring Cloud——OpenFeign

Spring Cloud——OpenFeign 一、OpenFeign能干嘛二、OpenFeign的基本使用三、Feign的高级特性1.OpenFeign超时控制2.OpenFeign重试机制3.OpenFeign之性能优化HttpClient54.OpenFeign请求和响应压缩5.OpenFeign之Feign日志打印 四、参考 OpenFeign是一个声明式的Web服务客户端。…

Android --- 消息机制与异步任务

在Android中&#xff0c;只有在UIThread(主线程)中才能直接更新界面&#xff0c; 在Android中&#xff0c;长时间的工作联网都需要在workThread(分线程)中执行 在分线程中获取服务器数据后&#xff0c;需要立即到主线程中去更新UI来显示数据&#xff0c; 所以&#xff0c;如…

程序员的五大方法论

前言&#xff1a; 最近看了一篇总结程序员学习&#xff0c;晋升方法的文章&#xff0c;颇有感想&#xff0c;决定分享给大家&#xff0c;原文地址&#xff1a;给程序员的5条学习方法论 (qq.com)https://mp.weixin.qq.com/s/xVFlF9qTf9c74Emmdm0DqA 在繁忙的工作中&#xff0c;持…

如何在postman上提交文件格式的数据

如何在postman上提交文件格式的数据 今天在写一个文件上传的功能接口时&#xff0c;想用postman进行提交&#xff0c;花了些时间才找到在postman提交文件格式的数据。记录一下吧&#xff01; 1.打开postman&#xff0c;选择POST提交方式&#xff0c;然后在Params那一行的Head…

代码随想录算法训练营DAY45|C++动态规划Part7|70.爬楼梯(进阶版)、322. 零钱兑换、279.完全平方数

文章目录 70.爬楼梯&#xff08;进阶版&#xff09;322. 零钱兑换思路CPP代码 279.完全平方数思路CPP代码 70.爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯 文章讲解&#xff1a;70.爬楼梯(进阶版) 322. 零钱兑换 力扣题目链接 文章讲解&#xff1a;322…

安装英伟达nvidia p4计算卡驱动@FreeBSD14

FreeBSD也能跑cuda AI训练拉&#xff01; 在FreeBSD安装好pytorch和飞桨cpu版本后&#xff0c;尝试安装英伟达nvidia p4计算卡驱动。毕竟全靠cpu速度太慢了&#xff0c;还是GPU快啊&#xff01;在磕磕绊绊几天后&#xff0c;终于成功成功安装好nvidia p4的cuda驱动&#xff0c…

从零开始:Django项目的创建与配置指南

title: 从零开始&#xff1a;Django项目的创建与配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 后端开发 tags: DjangoWebDevPythonORMSecurityDeploymentOptimization Django简介&#xff1a; Django是一个开源的高级Python Web框架&#xff…

The Role of Subgroup Separability in Group-Fair Medical Image Classification

文章目录 The Role of Subgroup Separability in Group-Fair Medical Image Classification摘要方法实验结果 The Role of Subgroup Separability in Group-Fair Medical Image Classification 摘要 研究人员调查了深度分类器在性能上的差异。他们发现&#xff0c;分类器将个…

PHP源码_最新在线工具箱网站系统源码

项目运行截图 源码贡献 https://githubs.xyz/boot?app41 部分数据库表 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for toolbox_category -- ---------------------------- DROP TABLE IF EXISTS toolbox_category…

【网络原理】HTTP 协议的基本格式和 fiddler 抓包工具的用法

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 【网络…

下载安装 VisualVM

1、下载安装 VisualVM 第1步&#xff1a;下载地址&#xff1a;https://visualvm.github.io/ 第2步&#xff1a;解压到制定位置 第3步&#xff1a;指定jdk路径 下载完成后&#xff0c;在etc文件夹下找到visualvm.conf文件&#xff0c;设置jdk路径visualvm_jdkhome"D:\ITS…