【深度強化學習】關于同一設備上cuda和gpu計算結果不一致問題

文章目錄

  • 問題描述
    • 關于seed: 跟原文一致
    • 補充:萬能seed
  • 問題結論
  • cpu和gpu差異來源分析
    • 浮點數精度的差異
    • 補充報錯:Expected all tensors to be on the same device!
    • 常見運算上的差異
      • 累加運算的差異
      • exp運算的差異
      • matmul運算的差異
    • forward上的差異(激活函數的運算差異)
      • 補充: .to(device)在普通張量和類上的差異:
    • backward上的差異
  • 總結或建議
  • 補充:寫在后面


打假:基于pytorch的代碼在GPU和CPU上訓練時,訓練輸出結果不同問題
這里他說的話是沒錯的,但結論是錯的,訓練結果還是會不一致的。

問題描述

發現控制隨機種子和所有超參數后,以及隨機數都讓cpu來產生,但實際運行后還是發現,同樣的代碼,結果還是不一致的問題。

代碼:ppo代碼(原代碼為cuda版本)
動手學強化學習 github代碼
只將device修改為如下,就為cpu版本

device = torch.device("cpu")    

此時上一版為cpu版本。

為了保持在環境采樣中選擇的一致性,在采樣時還是選擇了cpu來采樣,只有訓練模型時利用gpu來訓練。此時為cpu+gpu版本。
結果每輪eposide的return如下:第一行為cpu+gpu版本。第二行cpu版本。
在這里插入圖片描述
在第56列數據時出現了數據不一樣的結果。

當然這里也驗證了cuda版本的情況:
在第一個return數據就和cpu版本的不一致了。
(action第二個就不一致了,原因:第1,選擇action時,使用forward了(即:actor(state)),利用cpu的forward 和利用cuda的forward,在結果上有細小差別(后面證明)。第2,后續更新還是利用了forward,加大了區別。)

關于seed: 跟原文一致

不考慮在不同設備上復現的話,只考慮在同一設備上復現的話:
在此代碼:只需設置

torch.manual_seed(0) 
以及環境的隨機種子env.reset(seed=0)

因為使用的是to.(device)代碼,所以都是從cpu上隨機產生隨機數字,然后再移到其他設備,所以這里不需要設置cuda生成隨機數相同。
舉例:

import os
import torch
import torch.nn as nn
import torch.optim as optim
torch.manual_seed(0)# 定義一個簡單的模型
class SimpleModel(nn.Module):def __init__(self):super(SimpleModel, self).__init__()self.linear = nn.Linear(1, 1)def forward(self, x):return self.linear(x)# 創建模型和優化器
model = SimpleModel().to("cuda") #加不加to("cuda")結果一樣# 輸出模型參數
print("Model weights:", model.linear.weight.item())
### Model weights: -0.007486820220947266

補充:萬能seed

使用在cuda上生成的隨機數的情況:
需設置:

torch.cuda.manual_seed(seed) 

在這里插入圖片描述
若是有多個gpu:
則需設置

torch.cuda.manual_seed_all(seed)

由于每個gpu的性能和硬件不同而導致的選擇神經網絡加速算法不同
cudnn:CUDA Deep Neural Network library(gpu加速庫)
還需設置:

    torch.backends.cudnn.deterministic = Truetorch.backends.cudnn.benchmark = False torch.backends.cudnn.enabled = False

意思是不適用cudnn加速庫,使用確定性算法,且不選擇速度最快的一次。
默認為:
在這里插入圖片描述
也就是說,大多數情況下,第二個基準算法為了追求快,不追求同一設備每次結果一致的話可以設置為True,當然默認我們想要同一設備上結果一致,默認為False。

如果有import random
還需設置

random.seed(seed)

另外的配置:

哈希種子(沒看出加了和沒加有什么區別)
另外這個值在環境中默認是沒有的。

os.environ['PYTHONHASHSEED'] = str(seed)

猜測:
這里是為Python腳本配置一個確定的哈希值。
就像:給一個值一個哈希數一樣

# 隨機一個hash值
print(hash('hello')) #-1149467266229358681

另外的兩個配置,詳細見:最靠譜的pytorch訓練可復現性設置方案
萬能seed:

def set_seed(seed):# seed init.random.seed(seed)np.random.seed(seed)os.environ['PYTHONHASHSEED'] = str(seed)# torch seed init.torch.manual_seed(seed)torch.cuda.manual_seed(seed)torch.cuda.manual_seed_all(seed)torch.backends.cudnn.deterministic = Truetorch.backends.cudnn.benchmark = Falsetorch.backends.cudnn.enabled = False # train speed is slower after enabling this opts.# https://pytorch.org/docs/stable/generated/torch.use_deterministic_algorithms.htmlos.environ['CUBLAS_WORKSPACE_CONFIG'] = ':16:8'# avoiding nondeterministic algorithms (see https://pytorch.org/docs/stable/notes/randomness.html)torch.use_deterministic_algorithms(True)set_seed(11)

還要加上環境里的env 的seed。env.reset(seed =0 ) #gym0.26.2版本后

問題結論

參考:cuda官方文檔

根據NVIDIA的文檔,CPU和GPU計算結果不一致的原因主要與浮點數的表示和運算方式有關。總結如下:

1、浮點數表示差異:CPU和GPU使用不同的浮點數表示標準,如IEEE
754。這些標準在處理浮點數時可能會有細微的差異,尤其是在處理極端情況或邊界值時。

2、舍入誤差:在浮點數運算中,由于精度限制,舍入誤差是不可避免的。CPU和GPU可能在舍入策略上有所不同,導致結果的微小差異。

3、運算精度:CPU和GPU可能在支持的運算精度上有所不同。例如,某些GPU可能更傾向于使用單精度浮點數,而CPU可能更傾向于使用雙精度浮點數。這種差異可能導致計算結果的不同。

4、硬件架構差異:CPU和GPU的硬件架構差異可能導致它們在執行相同計算任務時的性能和準確性有所不同。GPU通常設計為并行處理大量數據,而CPU則更側重于通用計算。

5、優化和近似算法:在某些情況下,GPU可能會使用特定的優化或近似算法來提高性能,這可能會影響計算結果的準確性。

6、軟件和驅動程序差異:CPU和GPU的軟件棧和驅動程序也可能導致計算結果的差異。不同的編譯器優化、庫函數實現等都可能影響最終的計算結果。

說人話,就是這種計算結果不一致基本避免不了。

我單步調試后確實如此,兩者幾乎采取的隨機數都是一樣的

在我的兩個例子中:打印return的結果:
在這里插入圖片描述
發現前面55個計算結果都一樣,而后面的數開始不一樣了。

cpu和gpu差異來源分析

浮點數精度的差異

先普及一下浮點數含義:

參考:
float的精度和取值范圍

import numpy as np
print(0.1 * 6)
print(6 / 10 )
a = 0.1 * 6
print(np.array([a])[0]) #默認是float64
print(np.array([a], dtype=np.float32)[0])  # 32位浮點數可以表示小數點后7位
print('{}'.format(np.array([a], dtype=np.float32)[0]))  # 32位浮點數可以表示小數點后7位
print(np.array([a], dtype=np.float64)[0])   # 64位浮點數可以表示小數點后16位
print(np.array([a], dtype=np.float32)[0]+0.0000000000000001)   
print(np.array([a], dtype=np.float64)[0]+0.0000001)
print('{:.8f}'.format(1e-8))
print(0.6 - np.array([a], dtype=np.float32)[0])
'''
0.6000000000000001
0.6
0.6000000000000001
0.6
0.6000000238418579
0.6000000000000001
0.600000023841858
0.6000001
0.00000001
-2.384185793236071e-08
'''
print(0.1 *6 )   #小數后有16位,精確到15位
print(0.1 * 0.1)   #小數后有18位,精確到17位
print(0.1 * 3)  #小數后有17位,精確到16位
## 所以 float32 可以精確到小數點后7位,float64可以精確到小數點后15-17 位
'''
0.6000000000000001
0.010000000000000002
0.30000000000000004'''

結論:float32 可以精確到小數點后7位,
float64可以精確到小數點后15-17 位。

上述cuda官方上有說,cpu可能是可以精確到float80位,然后截斷到64位,所以可能會運算的比cuda更加精確。
但實際上:
在這里插入圖片描述
兩者的在同一浮點精度下,得到的數是相等的。

為了避免這個偏差,我這里設置為相同的精度運算方法float32,且每一個數都由cpu產生。(默認cpu產生,即去掉了device=的操作)

補充報錯:Expected all tensors to be on the same device!

注意:張量的操作都只能同時在同一設備上進行,否則報錯。

這個操作往往很難發現:
比如:下面這種情況卻不會報錯。因為PyTorch 在執行這種操作時會自動處理設備不匹配。它會將所有的操作數移動到同一設備上,然后執行操作。
在這里插入圖片描述

而我如果進行大量的操作時:就會出來,所以還是要分開來運算。

在這里插入圖片描述
為了避免出現因為pytorch自動的處理,和為了累計誤差,我下面都會盡量使用大量的操作。

常見運算上的差異

這里看下常見的加法,冪運算exp,矩陣乘法matmul運算上的差異

import torch
import gym
torch.manual_seed(0)
env_name = 'CartPole-v1' #動作是
env = gym.make(env_name)
s = env.reset(seed=0)
# print(s)
print('原始值:',s[0]) #tensor
print('原始值的為float32,小數后7位為精確值,第8位開始亂數字,例:第一個值為{}'.format(s[0][0])) #numpy.float32
s_cpu = torch.tensor(s[0], dtype=torch.float32).to('cpu')
s_cuda = torch.tensor(s[0], dtype=torch.float32).to('cuda')for i in range(10000): #1000時沒有nn =torch.randn(4, dtype=torch.float32)s_cpu +=  torch.exp(nn.to('cpu'))s_cuda += torch.exp(nn.to('cuda'))
s_cuda_cpu = s_cuda.to('cpu')
print(s_cpu)
print(s_cuda)
print(s_cuda_cpu)
print("torch.exp累加誤差:",torch.max(torch.abs(s_cuda_cpu - s_cpu)).item())print('原始值:',s[0]) #tensor
s_cpu = torch.tensor(s[0], dtype=torch.float32).to('cpu')
s_cuda = torch.tensor(s[0], dtype=torch.float32).to('cuda')
for i in range(10000): #nn =torch.randn(4, dtype=torch.float32)s_cpu +=  nn.to('cpu')s_cuda += nn.to('cuda')
s_cuda_cpu = s_cuda.to('cpu') #將CUDA結果移回CPUprint(s_cpu)
print(s_cuda)
print(s_cuda_cpu)
print("隨機數累加誤差:",torch.max(torch.abs(s_cuda_cpu - s_cpu)).item())
#print(torch.max(torch.abs(s_cuda_cpu - s_cuda).item())) #報錯: cuda和cpu的tensor不能同時運算print('原始值:',s[0]) #tensor
s_cpu = torch.tensor(s[0], dtype=torch.float32).to('cpu')
s_cuda = torch.tensor(s[0], dtype=torch.float32).to('cuda')
for i in range(2): #1000時沒有nn =torch.randn(4, dtype=torch.float32)s_cpu +=  torch.matmul(nn.to('cpu'),nn.to('cpu').t())s_cuda += torch.matmul(nn.to('cuda'),nn.to('cuda').t())
s_cuda_cpu = s_cuda.to('cpu')
print(s_cpu)
print(s_cuda)
print(s_cuda_cpu)
print("torch.matmul累加誤差:",torch.max(torch.abs(s_cuda_cpu - s_cpu)).item())

結果為:
在這里插入圖片描述
在累加的操作下,差異幾乎為0
冪運算的操作下,會出現差異
矩陣運算的操作下,會出現差異

我在實驗的時候發現,將第一個和第二個順序互換,有時會發現,exp的差異為0。

累加運算的差異

為了繼續驗證exp的運算有差異:
使隨機的數增多:
在這里插入圖片描述
結果:有差異了。

exp運算的差異

為了繼續驗證exp的運算有差異:
使隨機的數增多:

import torchtorch.manual_seed(0)
# 創建一個隨機張量
x = torch.randn(10000, dtype=torch.float32)
# 在CPU上進行累加
x_cpu = x.clone() #.clone() 復制張量
sum_cpu = torch.zeros_like(x_cpu)
#print(sum_cpu)
for _ in range(10000):sum_cpu += torch.exp(x_cpu)   #torch.mean()
# 在CUDA上進行累加
if torch.cuda.is_available():x_cuda = x.to('cuda')sum_cuda = torch.zeros_like(x_cuda).to('cuda') #zeros_like()創建一個與輸入張量相同大小的全0張量#print(sum_cuda)for _ in range(10000):sum_cuda += torch.exp(x_cuda)# 將CUDA結果移回CPUsum_cuda_cpu = sum_cuda.to('cpu')# 計算差異diff = torch.max(torch.abs(sum_cpu - sum_cuda_cpu))print(f"Max difference between CPU and CUDA: {diff.item()}")
else:print("CUDA is not available.")

結果:差異變的很明顯了。
在這里插入圖片描述

matmul運算的差異

同理:

import torch
torch.manual_seed(0)
# 創建一個隨機張量
x = torch.randn(1000, 1000, dtype=torch.float32)
# 在CPU上計算
x_cpu = x.to('cpu')
result_cpu = torch.matmul(x_cpu, x_cpu.t()) #矩陣乘法
# 在CUDA上計算
if torch.cuda.is_available():x_cuda = x.to('cuda')result_cuda = torch.matmul(x_cuda, x_cuda.t())# 將CUDA結果移回CPUresult_cuda_cpu = result_cuda.to('cpu')# 計算差異diff = torch.max(torch.abs(result_cpu - result_cuda_cpu))print(f"Max difference between CPU and CUDA: {diff.item()}")
else:print("CUDA is not available.")

差異為:
在這里插入圖片描述
差異也變大了。

`結論:這里僅簡單得到一些常見的運算差異,據此推測,其他的運算在大量的運算下,差異會越來越明顯。

forward上的差異(激活函數的運算差異)

這里用一個最簡單的單層qnet表示其差異,激活函數為relu。

.cuda() 和to(‘cuda’)的結果一樣,只是to(‘cuda:0’),可以指定哪個gpu,我這里單個gpu,所以兩個函數一個意思。

代碼: 因為這里沒有用到反向傳播forward,所以在第一次forward后,模型的實例沒有變。

import torch
import torch.nn.functional as F# 設置隨機種子
torch.manual_seed(0)# 定義模型
class ValueNet(torch.nn.Module):def __init__(self, state_dim, hidden_dim):super(ValueNet, self).__init__()self.fc1 = torch.nn.Linear(state_dim, hidden_dim)self.fc2 = torch.nn.Linear(hidden_dim, 1)def forward(self, x):x = F.relu(self.fc1(x))return self.fc2(x)# 設置模型和數據
state_dim = 10
hidden_dim = 20
model = ValueNet(state_dim, hidden_dim)
input_data = torch.randn(100, state_dim,dtype=torch.float32)  # 假設有100個樣本# 在CPU上運行
model.cpu()
input_data = input_data.cpu()
cpu_output = model(input_data)# 在GPU上運行
if torch.cuda.is_available():model.cuda()input_data = input_data.cuda()gpu_output = model(input_data)
else:print("No GPU available")# 比較輸出結果
if torch.cuda.is_available():# 將GPU輸出轉移到CPU上進行比較gpu_output_cpu = gpu_output.cpu()# 計算差異difference = torch.abs(cpu_output - gpu_output_cpu)print("Max difference:", difference.max().item())print("Average difference:", difference.mean().item())
else:print("No GPU available for comparison")

單次運行結果差異:
在這里插入圖片描述

補充: .to(device)在普通張量和類上的差異:

張量的to(device):它會創建一個新的張量副本,并將這個副本移動到指定的設備上。原始張量不會被修改。

在類(模型)的to(device):修改模型實例的存儲位置(參數和緩沖區),模型實例本身并不會被重新創建。

所以上文的代碼可以寫成這樣:

## gpu 與 cpu 差異來源分析
import torch
import torch.nn.functional as F
import copy# 設置隨機種子
torch.manual_seed(0)# 定義模型
class ValueNet(torch.nn.Module):def __init__(self, state_dim, hidden_dim):super(ValueNet, self).__init__()self.fc1 = torch.nn.Linear(state_dim, hidden_dim)self.fc2 = torch.nn.Linear(hidden_dim, 1)def forward(self, x):x = F.relu(self.fc1(x))return self.fc2(x)# 設置模型和數據
state_dim = 10
hidden_dim = 20
model = ValueNet(state_dim, hidden_dim)
input_data = torch.randn(100, state_dim,dtype=torch.float32)  # 假設有100個樣本
model_cpu = copy.deepcopy(model) #復制模型
model_cuda = model.to('cuda')
print("Model_cpu weights:", model_cpu.fc1.weight[0][0])
print("Model_gpu weights:", model_cuda.fc1.weight[0][0])
# 在CPU上運行input_data_cpu = input_data.to('cpu')
input_data_cuda = input_data.to('cuda')cpu_output = model_cpu(input_data_cpu)gpu_output = model_cuda(input_data_cuda)gpu_output_cpu = gpu_output.to('cpu')
# 計算差異
difference = torch.abs(cpu_output - gpu_output_cpu)
print("Max difference:", difference.max().item())
print("Average difference:", difference.mean().item())

在這里插入圖片描述
確實保證了模型的參數一致,有了差異。

backward上的差異

為了剔除掉forward()函數上的激活函數影響,這里使用最簡單的模型

# 定義一個簡單的線性模型
model = nn.Linear(1, 1) # 輸入維度是1,輸出維度是1

來驗證其差異性:

# 探討backward()函數的差異
import torch
import torch.nn as nn
import torch.optim as optim
import copy
#設置隨機種子
torch.manual_seed(0)# 生成一些隨機的輸入和標簽
x = torch.randn(100, 1,dtype=torch.float32) # 100個樣本,每個樣本有1個特征  randn
y = 3 * x + 5 + torch.randn(100, 1,dtype=torch.float32) # 100個樣本,每個樣本有1個標簽,服從 y = 3x + 5 + 噪聲 的分布# 定義一個簡單的模型
class SimpleModel(nn.Module):def __init__(self):super(SimpleModel, self).__init__()self.linear = nn.Linear(1, 1)def forward(self, x):return self.linear(x)
# 定義一個簡單的線性模型
model = nn.Linear(1, 1) # 輸入維度是1,輸出維度是1
#model = SimpleModel()  # 也可以使用此模型,結果一樣,但最好不要同時設置,即 model = nn.Linear(1, 1) ; model = SimpleModel()
#因為每次調用 nn.Linear 都會使用隨機數生成器來初始化權重,會使結果不一致
model_cpu = copy.deepcopy(model)
model_cuda = model.to('cuda')   
# 定義一個均方誤差損失函數
criterion = nn.MSELoss()
# 定義一個隨機梯度下降優化器
optimizer_cpu = optim.SGD(model_cpu.parameters(), lr=0.01) # 學習率是0.01
optimizer_cuda = optim.SGD(model_cuda.parameters(), lr=0.01) # 學習率是0.01epochs = 1000
# 在CPU上訓練
for epoch in range(epochs):output = model_cpu(x)loss = criterion(output, y)optimizer_cpu.zero_grad()loss.backward()optimizer_cpu.step()
# 在GPU上訓練
for epoch in range(epochs):output = model_cuda(x.to('cuda'))loss = criterion(output, y.to('cuda'))optimizer_cuda.zero_grad()loss.backward()optimizer_cuda.step()
model_cuda_cpu = model_cuda.to('cpu')x_ = torch.randn(100, 1,dtype=torch.float32)
#print(x_)
cpu_output = model_cpu(x_)
gpu_output = model_cuda_cpu(x_)diff =torch.max(torch.abs(cpu_output - gpu_output)).item()
print("Max difference:{}".format(diff))
# 打印模型參數
print( 'cpu上:',model_cpu.weight.item(), model_cpu.bias.item()) #用SimpleModel()時,要改為model_cpu.linear.weight.item(), model_cpu.linear.bias.item()
print('cuda上:',model_cuda.weight.item(), model_cuda.bias.item())

結果為:
在這里插入圖片描述
在epoch為1000時,差異還是0,在10000時就有了細小的差異。

總結或建議

1、在一般使用中,可以不必追求cpu和gpu計算的結果一致性,也也避免不了,且cpu和gpu導致的細小差別,在訓練的效果上幾乎沒有區別。

2、同時,在同一臺設備上,我們盡量要求該程序的結果能復現,是為了更好修改超參數。(見:本文萬能seed,適用于單機多卡)

3、不必追求在不同的設備上能復現一致結果,最終的效果在相同的超參數和輸入下,輸出的結果也相差無幾。

4、cpu和gpu在設計時的目的也不同,有差異理所應當,這里本文只分析了在計算上和訓練神經網絡時的出現的差異,僅作參考。

補充:寫在后面

–24.5.24
發現了一個區別:
還是此代碼:
代碼:ppo代碼(原代碼為cuda版本)
動手學強化學習 github代碼

現象:
在環境’CartPole-v0’的情況下:
我將while not done : 改成了 如下情況
在這里插入圖片描述
發現在cpu版本的情況下,其他參數不變,依然能夠收斂;
在這里插入圖片描述

而cuda版本下,就不收斂了。
在這里插入圖片描述
而cpu+cuda 的版本也不收斂。

這里的情況就是在done這個狀態相當于沒有的情況下,cpu依然能夠學習到可以收斂的參數。
結論:
1、猜測cpu的計算精度確實比cuda要高的多。
2、狀態的設置對于模型的收斂至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14454.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14454.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14454.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【LeetCode 隨筆】面試經典 150 題【中等+困難】持續更新中。。。

文章目錄 189. 輪轉數組122. 買賣股票的最佳時機 II55. 跳躍游戲45. 跳躍游戲 II274. H 指數 🌈你好呀!我是 山頂風景獨好 💝歡迎來到我的博客,很高興能夠在這里和您見面! 💝希望您在這里可以感受到一份輕松…

機器學習云環境搭建

在 https://support.huaweicloud.com/browsertg-obs/obs_03_1003.html 下載對應版本的 OBS Broswer 軟件,如圖,紅框內的為安裝文件,藍色框內的為對應安裝文件的校驗文件(無需下載) 以 64 位機為例,下載完…

景源暢信電商:抖店需要的成本高嗎?

在數字化時代的浪潮中,短視頻平臺迅速崛起,成為連接用戶與商家的新橋梁。抖音作為其中的佼佼者,不僅改變了人們的娛樂方式,也催生了新型的電商模式——抖店。許多人好奇,入駐這樣一個充滿活力的平臺,需要承…

618知識狂歡,挑本好書,點亮智慧生活!

618精選編程書單:提升你的代碼力 一年一度的618又到啦!今年的618就不要亂買啦,衣服買多了會被淘汰,電子產品買多了會過時,零食買多了會增肥,最后怎么看都不劃算。可是如果你購買知識,堅持閱讀&a…

第N2周:Embeddingbag與Embedding詳解

🍨 本文為🔗365天深度學習訓練營 中的學習記錄博客🍖 原作者:K同學啊 | 接輔導、項目定制🚀 文章來源:K同學的學習圈子 目錄 什么是詞嵌入? Embedding與EmbeddingBag詳解 Embedding Embeddi…

代碼隨想錄算法訓練營第十七天|LeetCode110 平衡二叉樹、LeetCode257 二叉樹的所有路徑

題1: 指路:LeetCode110 平衡二叉樹 思路與代碼: 左右子樹的高度差小于等于1。對于這個題,遞歸比迭代方便太多,我也想過迭代,但是我沒有寫出來,大家可以自己試一下。遞歸代碼如下:…

如何為ChatGPT編寫有效的提示詞:軟件開發者的指南

作為一名軟件開發者,特別是使用Vue進行開發的開發者,與ChatGPT等AI助手高效互動,可以極大地提升你的開發效率。本文將深入探討如何編寫有效的提示詞,以便從ChatGPT中獲取有用的信息和幫助。 1. 明確目標 在編寫提示詞之前&#…

后端之路第二站(正片)——SprintBoot之:分層解耦

很抽象,我自己也不好理解,僅作為一個前端轉后端的個人理解 一、先解釋一個案例,以這個案例來分析“三層架構” 這里我先解釋一下黑馬程序員里的這個案例,兄弟們看視頻的可以跳過這節課:Day05-08. 請求響應-響應-案例_…

【webrtc】m98:Call的創建及Call對音頻接收處理

call中多個流共享相同的輔助組件 這幾個是與外部共用的 線程傳輸send控制module 線程任務隊列工廠call的輔助組件中各種統計以及接收測的cc是自己創建的 call自己的多個輔助組件是外部傳遞來的 call 創建多個接收流 這里用一個set 來保存所有指針,并沒有要map的意思:

【因果推斷從入門到精通二】隨機實驗3

目錄 檢驗無因果效應假說 硬幣投擲的特殊性何在? 檢驗無因果效應假說 無因果效應假說認為,有些人存活,有些人死亡,但接受mAb114治療而不是ZMapp與此無關。在174例接受mAb14治療的患者中,113/17464.9%存活了28天&…

【MySQL精通之路】InnoDB(6)-磁盤結構

主要博客: 【MySQL精通之路】InnoDB存儲引擎-CSDN博客 1 表 2 索引 【MySQL精通之路】InnoDB(6)-磁盤結構(2)-索引-CSDN博客 3 表空間 【MySQL精通之路】InnoDB(6)-磁盤結構(3)-表空間-CSDN博客 4 雙寫緩沖區 【MySQL精通之路】InnoDB(6)-磁盤結構(4)-雙寫緩沖…

修改MySQL root用戶密碼

ALTER USER ‘root’‘localhost’ IDENTIFIED BY ‘new_password’; ALTER USER ‘root’‘%’ IDENTIFIED BY ‘new_password’; 》 SET GLOBAL read_only OFF; select * from mysql.user;

Java入門基礎學習筆記47——ArrayList

什么是集合呢? 集合是一種容器,用來裝數據的,類似數組。 有數組,為什么還要學習集合呢? 數組定義完成并啟動后,長度就固定了。 而集合是大小可變,開發中用的最多的。 集合的特點:大…

匯聚榮科技有限公司優點有哪些?

在當今快速發展的科技時代,企業之間的競爭愈發激烈。作為一家專注于科技創新與研發的公司,匯聚榮科技有限公司憑借其卓越的技術實力和創新能力,在業界樹立了良好的口碑。那么,匯聚榮科技有限公司究竟有哪些優點呢?接下來&#xf…

C++利用TinyXML讀取XML文件

TinyXML是什么? TinyXML是一個輕量級的C XML解析器,它提供了一種簡單的方法來解析和操作XML文檔。TinyXML被設計為易于使用和集成到C項目中,并且非常適合處理小型XML文件。 以下是TinyXML的一些主要特點和優點: 輕量級: T…

OSPF問題

.ospf 選路 域內 --- 1類,2類LSA 域間 --- 3類LSA 域外 --- 5類,7類LSA --- 根據開銷值的計算規則不同,還分為類型1和類型2 ospf 防環機制 區域內防環:在同一OSPF區域內,所有路由器通過交換鏈路狀態通告&#xff…

VUE面試題(3)--vue常見面試題

1.vue優點 低耦合。視圖(View)可以獨立于Model變化和修改,一個ViewModel可以綁定到不同的"View"上,當View變化的時候Model可以不變,當Model變化的時候View也可以不變。 可重用性。你可以把一些視圖邏輯放在一個ViewModel里面,讓很多view重用這段視圖邏輯。 …

226.翻轉二叉樹

翻轉一棵二叉樹。 思路: 指針做交換 用遞歸(前序or后序,中序不行) 前序:中左右 遍歷到“中”的時候,交換它的左右孩子 然后分別對它的左孩子和右孩子使用“交換函數”(定義的)&a…

【網絡版本計算器的實現】

本章重點 理解應用層的作用, 初識HTTP協議理解傳輸層的作用, 深入理解TCP的各項特性和機制對整個TCP/IP協議有系統的理解對TCP/IP協議體系下的其他重要協議和技術有一定的了解學會使用一些分析網絡問題的工具和方法 ?注意!! 注意!! 注意!! 本課是網絡編程的理論基礎.是一個服務…

零基礎學Java第二十二天之迭代器 Iterator

迭代器 Iterator 的理解和相關集合 使用 1、理解 迭代器(Iterator)是設計模式中的一種,它允許程序員遍歷容器(例如列表、集合等)中的元素,而無需了解容器底層的實現細節。在編程中,迭代器提供了…