Dive into DeepLearning

數學符號說明

$z = g \circ f $就是z(x) = g(f(x)),也就是$y = f(x), z = g(y)$

https://math.stackexchange.com/a/1092727
$x \in R$ : x是一個(一維)實數純量,例如
$x =-2$ 或 $x=42$
$x \in R^{n*d}$

Ch2

2.5 Automatic Diff erentiation

現代的深度學習都有提供automatic differentiation(autograd)和backpropagation的功能,下面將介紹如何使用

2.5.2 Backward for Non-Scalar Variables

為什麼要先sum()之後才backpropagation?

cs231n
http://cs231n.stanford.edu/handouts/derivatives.pdf

  • Scalar in Scalar out: 利用chain rule就可以計算backpropagation當函式為$f, g : R \rightarrow R$且$z = (g \circ f)(x)$則$\frac{\partial z}{\partial x}=\frac{\partial z}{\partial y}\frac{\partial y}{\partial x}$。這告訴我們我們移動x一點點$\Delta_x$則y移動的量如下$$\Delta_y=\frac{\partial y}{\partial x}\Delta_x$$,而z移動的量為$$\frac{\partial z}{\partial y}\Delta_y=\frac{\partial z}{\partial y}\frac{\partial y}{\partial x}\Delta_x$$

  • Vector in, scalar out: 利用Gradient可以計算backpropagation
    當函式為$f : R^N \rightarrow R$且$x \in R^N$也就是說x是一個vector,而$\nabla_xf(x) \in R^N$,也就是Gradient的結果也是一個vector。繼續利用前面chain rule的概念$$x\rightarrow x+\Delta_x \Rightarrow y \rightarrow \approx y+\frac{\partial y}{\partial x} \cdot \Delta_x$$不過現在的狀況$x$, $\Delta_x$, \frac{\partial y}{\partial x} 都是vector,而兩個vector的內積剛好是scalar

  • Vector in, Vector out
    現在函式$f:R^N \rightarrow R^M$

  • Jacobian
    linear map
    matrix代表了linear map,而determinant代表linear map之後面積放大的倍率,而負的determinant代表座標方向反轉

  1. 微分不應該單純的想成斜率,積分也不應該單純想成面積。而是用另以種角度來想,微分代表在某一個點上,把它放很大來看之後他的線性映射是如何(linear map)

  2. 對二維函式來說,Jacobian matrix就是在(a, b)附近的linear map

https://youtu.be/wCZ1VEmVjVo
https://youtu.be/CfW845LNObM
https://angeloyeo.github.io/2020/07/24/Jacobian_en.html

CH7

7.2.1 The Cross-Correlation Operation

經過Cross-Correlation Operation後,輸出的tensor尺寸為
$(n_h − k_h + 1 ) × (n_w − k_w + 1 )$

7.2.2 Convolutional Layers

Convolutional Layers就是經過Cross-Correlation Operation之後的tensor對每一個element都加上一個bias。Conv的kernel如同前面MLP的權重,初始化的時候我們是用亂數初始化

7.2.3 Object Edge Detection in Images

已知一個人工製作的邊緣偵測器kernel為[1, -1]可以偵測垂直線,等一下會嘗試讓電腦自己學習出這個kernel

7.2.4 Learning a Kernel

接下來我們要常識讓電腦自動學習kernel,為了簡單起見bias為0。(為什麼先sum再backward?)(https://www.youtube.com/watch?v=Q7KekwUricc)(https://dlvu.github.io/)
以下程式可以自動學習kernel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Construct a two-dimensional convolutional layer with 1 output channel and a
# kernel of shape (1, 2). For the sake of simplicity, we ignore the bias here

import torch
from torch import nn
from conv import corr2d

X = torch.tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.]])

Y = torch.tensor([[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.]])

X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
lr = 3e-2 # Learning rate
conv2d = nn.LazyConv2d(1, kernel_size=(1, 2), bias=False)

for i in range(10):
Y_hat = conv2d(X)
l = (Y_hat - Y) ** 2
conv2d.zero_grad()
l.sum().backward()
# Update the kernel
conv2d.weight.data[:] -= lr * conv2d.weight.grad
if (i + 1) % 2 == 0:
print(f'epoch {i + 1}, loss {l.sum():.3f}')
print(conv2d.weight.data.reshape((1, 2)))

PyTorch Lazy modules的用途:

可以避免寫死in_features,對於動態的in_features來說比較方便
https://jarvislabs.ai/blogs/PyTorch-lazy-modules/

7.2.5 Cross-Correlation and Convolution

再Deeplearning 使用的Convolution計算方式其實真正的名稱是Cross-Correlation,但是由於Cross-Correlation和Convolution的差別只在於kernerl上下和左右都互換,但對於traing的結果影響不大,所以依然稱為Convolution

7.2.6 Feature Map and Receptive Field

convolutional layer的輸出稱為Feature Map。
receptive field則是影響輸出結果之前的所有元素。()

7.3 Padding and Stride

單純使用conv會使得後面layer的input快速變小。Padding可以解決這個狀況,相反的Stride則是用來快速讓輸入變小。

7.3.1 Padding

padding和kernel關係的公式如下,假設row padding $p_h$, column padding $p_w$:
$$(n_h − k_h + p_h + 1 ) × (n_w − k_w + p_w + 1 )$$
我們可以藉由$p_h=k_h-1$和$p_w=k_w-1$讓輸入和輸出的長寬一樣。
如果$k$為奇數,則padding剛好可以填充到兩側,如果$k$為偶數則兩側的padding數量有一邊會多一個。

7.3.2 Stride

當高的stride為$s_h$寬的stride為$s_w$,則輸出為
$$\lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor$$

7.4 Multiple Input and Multiple Output Channels

7.4.1 Multiple Input Channels

當輸入的channel數量大於1的時候,每一個channel會需要至少一個kernel。假設現在輸入有3個channel,每一個channel分別對一個kernel之後相加結果就是輸出。

1
2
3
4
5
6
7
8
9
def corr2d_multi_in(X, K):
# Iterate through the 0th dimension (channel) of K first, then add them up
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
corr2d_multi_in(X, K)
# tensor([[ 56., 72.],
# [104., 120.]])

7.4.2 Multiple Output Channels

為了讓神經網路學到更多feature,我們嘗試讓同一層conv layer的kernel為三維而且第三個維度和輸入的channel數一樣$c_i\times k_h \times k_w$,例如輸入為3個channel,我們就設計kernel的第三個維度為3。由於每一組kernel最後的輸出都是一個channel。所以最後輸出的channel數就是kernel的組數。因此 convolution layer的維度就變成$c_o \times c_i \times k_h \times k_w$

  • stack()在這裡會把沿著指定的dimension把tensor堆疊
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def corr2d_multi_in_out(X, K):
    # Iterate through the 0th dimension of K, and each time, perform
    # cross-correlation operations with input X. All of the results are
    # stacked together
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)
    K = torch.stack((K, K + 1, K + 2), 0)
    K.shape
    #torch.Size([3, 2, 2, 2])
    corr2d_multi_in_out(X, K)
    # tensor([[[ 56., 72.],
    # [104., 120.]],
    # [[ 76., 100.],
    # [148., 172.]],
    # [[ 96., 128.],
    # [192., 224.]]])

7.4.3 1 × 1 Convolutional Layer

1 × 1 Convolutional Layer唯一能夠影像的就是channel這個維度。
1 × 1 Convolutional Layer可以視為在每一個單獨的像素位置上的channel的fully conection layer。並且將channel數由$c_i$轉換成$c_o$。

7.5 Pooling

Pooling讓我們可以專注在影像比較大的區域而不會因為影像中細微的變化就影響輸出結果。

7.5.1 Maximum Pooling and Average Pooling

池化層沒有任何參數,將window所有元素平均為Average Pooling,取出最大值則為Maximum Pooling。

7.5.2 Padding and Stride

跟conv layer一樣pooling也有padding和Stride,而由於pooling主要目的是從一個區域擷取資訊,所以深度學習框架預設將Stride的大小設定跟pooling window一樣大,不過我們還是可以自行修改大小。

7.5.3 Multiple Channels

不同於conv layer,pooling對每一個channel是分開處理的,而不像conv layer會把其他channel的結果相加。因此pooling的輸入和輸出channel數目是一樣的。

7.6 Convolutional Neural Networks (LeNet)

接下來介紹LeNet

7.6.1 LeNet

下面是LeNet的Pytorch實作,這裡使用了Xavier initialization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def init_cnn(module): #@save
"""Initialize weights for CNNs."""
if type(module) == nn.Linear or type(module) == nn.Conv2d:
nn.init.xavier_uniform_(module.weight)

class LeNet(d2l.Classifier): #@save
"""The LeNet-5 model."""
def __init__(self, lr=0.1, num_classes=10):
super().__init__()
self.save_hyperparameters()
self.net = nn.Sequential(
nn.LazyConv2d(6, kernel_size=5, padding=2), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.LazyConv2d(16, kernel_size=5), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Flatten(),
nn.LazyLinear(120), nn.Sigmoid(),
nn.LazyLinear(84), nn.Sigmoid(),
nn.LazyLinear(num_classes))

7.6.2 Training

雖然CNN的參數比較少,但是計算量並不少,用GPU計算更適合。

8 Modern Convolutional Neural Networks

8.1 Deep Convolutional Neural Networks (AlexNet)

硬體運算速度的提升增加了CNN可行性。

8.1.1 Representation Learning

有一派的研究員相信圖片中的feature是可以讓電腦自己學習得到的。AlexNet在地層layer所學到的kernel跟傳統機器學習手工製作的feature很像。
推動CNN兩大因素別是”資料”和”電腦硬體”,過去的資料集都很小而且電腦運算速度很慢。

8.1.2 AlenNet

AlexNet把sigmoid activation function改成ReLU activtion function。
ReLU activation function,讓模型訓練變得更加容易。因為如果初始化的參數很接近0或1,sigmoid activation function的輸出值會很接近0,這使的反向傳播幾乎沒有作用。而ReLU activation function卻不會有這種情形。
AlexNet在訓練的時候也大量的利用圖片擴增的技巧,這使得訓練結果不會overfitting。

8.1.4 Discussion

AlexNet弱點是他最後的兩個layer佔用了非常大量的記憶體和運算,這對需要高速運算的場景非常不利。
另外一個可以注意的點是即使AlexNet的參數量遠大於資料及照片總數,AlexNet也幾乎沒有Overfitting。這是因為有用到現代化的正規化方法如Dropout。

8.2 Networks Using Blocks (VGG)

神經網絡架構的設計日益抽象化,研究人員從思考個別神經元逐漸轉向整個層面,然後到塊狀結構,即層的重複模式。十年後,這已經進一步發展到研究人員使用完整的訓練模型,將它們重新應用於不同但相關的任務。這種大型 pretrained models通常被稱為foundation models 。
這種Blocks的概念最早出現在VGG network,使用迴圈和子程序,可以在任何現代深度學習框架中輕鬆地在代碼中實現這些重複的結構。

8.2.1 VGG Blocks

CNNs的基本構建塊是以下順序的序列:(i)帶填充的卷積層以保持解析度,(ii)如ReLU之類的非線性激活函數,(iii)如最大池化之類的池化層以減小解析度。這種方法的一個問題是空間解析度下降得相當迅速。特別是,這在所有維度(d)用完之前,對於網絡在卷積層上存在著 $log_2d$的硬限制。例如,在ImageNet的情況下,以這種方式不可能有超過8個卷積層。
Simonyan和Zisserman(2014)的關鍵想法是使用連續的小卷層來取代一個大卷積層,例如兩個3x3的卷積層其實涵蓋的像素跟一個5x5一樣大,經過觀察利用連續小的卷積層的效果跟直接用一個大卷積層的效果相似。
堆疊3×3的卷積後來成為後來的深度網絡的黃金標準,直到最近由Liu等人(2022)重新審查了這個設計決策。

  • VGG的構造:一連串的$3 \times 3$kernel且padding為1的conv layer最後接著一個$2 \times 2$ stride為2的max-pooling。下面程式實作一個VGG block,以$3 \times 3$kernel的數量num_convs和輸出channel數量out_channels作為參數。
1
2
3
4
5
6
7
def vgg_block(num_convs, out_channels):
layers = []
for _ in range(num_convs):
layers.append(nn.LazyConv2d(out_channels, kernel_size=3, padding=1))
layers.append(nn.ReLU())
layers.append(nn.MaxPool2d(kernel_size=2,stride=2))
return nn.Sequential(*layers)

8.2.2 VGG Network

VGG Network可以拆成兩個部分,前半段是多個conv layer和pooling layer組成,後半段是fully connected layer所組成。

下面程式定義了VGG network,在這裡我們利用for迴圈將多個VGG block組合成VGG netowrk

1
2
3
4
5
6
7
8
9
10
11
12
13
class VGG(d2l.Classifier):
def __init__(self, arch, lr=0.1, num_classes=10):
super().__init__()
self.save_hyperparameters()
conv_blks = []
for (num_convs, out_channels) in arch:
conv_blks.append(vgg_block(num_convs, out_channels))
self.net = nn.Sequential(
*conv_blks, nn.Flatten(),
nn.LazyLinear(4096), nn.ReLU(), nn.Dropout(0.5),
nn.LazyLinear(4096), nn.ReLU(), nn.Dropout(0.5),
nn.LazyLinear(num_classes))
self.net.apply(d2l.init_cnn)

8.3 Network in Network (NiN)

VGG, LeNet, VGG都有一個共通的缺點。

  1. fully connected layer有非常大的參數量。
  2. fully connected layer沒辦法放在模型最後面以外的地方。
    Network in Network解決了上面兩個問題。他利用了像面兩個方式解決。
  3. 利用$1 \times 1$ conv layer來取代fully connected layer
  4. 在模型的最後面使用global average pooling
    NiN的好處是沒有fully connected layer因此參數量大大減少。

8.3.1 NiN Blocks

由一個conv layer加上兩個$1 \times 1$ conv layer組成

8.4 Multi-Branch Networks (GoogLeNet)

GoogLeNet可以說是第一個將模型拆解成三個部位stem, body, head的模型。

8.4.1 Inception Blocks

Inception Blocks跟前面模型不一樣的是一個block有三個分支

8.5 Batch Normalization

Batch Normalization是一個能夠有效加速深度模型收斂的方法,與 residual blocks可以讓模型深度達到一百層。

8.5.1 Training Deep Networks

Batch normalization 可以被套用在單一個layer或是所有layer,如此一來對每一層的input都先做一次 normalization(也就是平均為0標準差為1)。
注意到如果我們套用batch normalization在minibatches為一的狀況下,我們不會學到任何東西,因為所有的 hidden unit都會變成0。
因此使用Batch normalization的時候batch size的大小變成十分重要。
Batch normalization中scale parameter 和shift parameter是透過模型學習而得。

8.5.2 Batch Normalization Layers

CH9 Recurrent Neural Network(RNN)

在這之前我們的資料長度都是固定的,在這章將要學習如何讓模型學習長度不固定的序列資料。
RNN利用recurrent connection讓模型可以學習序列化的資料,可以把他想成一個迴授迴路

9.1 序列化資料
在此之前我們的特徵向量長度是固定的,而序列化資料的特徵向量是以時間排序而且長度不固定的資料。

git刪除歷史中的大檔案

列出歷史中的大檔案

1
2
3
4
5
6
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
sed -n 's/^blob //p' |
sort --numeric-sort --key=2 |
cut -c 1-12,41- |
$(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

https://stackoverflow.com/questions/10622179/how-to-find-identify-large-commits-in-git-history

刪除大檔案

https://stackoverflow.com/questions/2100907/how-to-remove-delete-a-large-file-from-commit-history-in-the-git-repository

https://rtyley.github.io/bfg-repo-cleaner/

遇到protect branch

1
--no-blob-protection

https://stackoverflow.com/a/47421624

遇到 [remote rejected] 錯誤

https://stackoverflow.com/a/74373146

演算法讀書筆記

CH1

MergeSort: divide-and-conquer經典範例

輸入: 一個沒有規則的數字陣列。
輸出: 排序好的數字陣列
MergeSort是divide-and-conquer方法的展現。recursive divide-and-conquer algorithm是不斷的利用遞迴把問題拆變小,直到問題不能在拆分後再把結果合併回來。對於array把問題變小的方式最直覺就是把array拆兩半,拆到最後再慢慢把array合併回來。

下面是MergeSort的Pseudocode

1
2
3
4
5
6
7
Input: array A of n distinct integers.
Output: array with the same integers, sorted from
smallest to largest.
// ignoring base cases
C := recursively sort first half of A
D := recursively sort second half of A
return Merge (C,D)

這段程式碼有幾點可以注意

  • base cases: 也就是沒辦法再分割,沒辦法再繼續遞迴,返回它自身就是他的回傳值。
  • Pseudocode不包含實作細節,例如如果輸入是奇數個元素的狀況下,我們可以將多出的一個element放入一半被拆分的array。Pseudocode只記錄概念上的運作方式,並不會加入任何和個別程式語言有關的實作細節。

在上面的Pseudocode還有一個Merge函式,下面是關於Merge函式的Pseudocode。想法是將
排序好的C和D array依序再排序成新的排序過的array。

1
2
3
4
5
6
7
8
9
10
11
12
13
Merge
Input: sorted arrays C and D (length n/2 each).
Output: sorted array B (length n).
Simplifying assumption: n is even.
1 i := 1
2 j := 1
3 for k := 1 to n do
4 if C[i] < D[j] then
5 B[k] := C[i] // populate output array
6 i := i + 1 // increment i
7 else // D[j] < C[i]
8 B[k] := D[j]
9 j := j + 1

1.5 MergeSort: The Analysis

計算演算法速度的方式就是計算這個演算法總共需要執行多少次最基礎的計算。也就是計算Running Time

1.5.1 Running Time of Merge

Lemma 1.1 (Running Time of Merge) 對於合併兩個長度為$l/2$的array的,合併最多只會用到6l步(可從前面Merge的Pseudocode推論。)

1.5.2 Running Time of MergeSort

Theorem 1.2 (Running Time of MergeSort) : 對於每個長度$n\geq 1$的array, MergeSort的Merge步驟最多執行$6nlog_2n+6n$個步驟

1.5.3 Proof of Theorem 1.2

從recursion tree 可以看到,每多一個遞迴level,工作被分割的總數就是兩倍,但是工作的array長度是上一個level的一半。因此level-j的subproblems為$2^j$,而level-j地array長度為$n/{2^j}$。根據上一節的結論,對於每個長度$n\geq 1$的array, MergeSort最多執行$6nlog_2n+6n$個步驟。如果我們只看level-j遞迴呼叫以外的工作,就只剩下Merge步驟,因此每個level的運算步驟總共為$6n/2^j$
於是我們可以得到level-j所有的運算步驟總數為$2^j \cdot \frac{6n}{2^j}=6n$
而MergeSort總共會有$log_x^n+1$個level,因此整個MergeSort總共的步驟為$6nlog_2n+6n$

1.6 Guiding Principles for the Analysis of Algorithms

我們在計算演算法速度的時候會有三個原則,以下逐一介紹。因為在有些時候為了簡化計算,我們會做出一些取設,但是為了讓數學分析依然能夠預測演算法的速度,因此我們才用到下面三個原則。

Principle #1: Worst-Case Analysis

在前面推導的結果$6nlog_2n+6n$是不管n是什麼數字這個公式都能夠使用。也就是我們沒有對n做任何的假設。這種分析法稱為Worst-Case Analysis,因為這個演算法最慢的計算時間依然符合這個公式。
除了Worst-Case Analysis之外還有average-case analysis和 analysis of benchmark instances可以使用,但是這兩個使用的時候必須非常清楚所處理的問題的條件。

Principle #2: Big-Picture Analysis

在運行時間界限中,我們不應過於關注小的常數因子或低階項。

參考

Algorithms Illuminated

deepstream-tracker參數調整

Accuracy-Performance Tradeoffs

下面這些參數的調整將會影響到準確度和效能。

Visual Feature Types and Feature Sizes

Visual feature types

  • useColorNames
  • useHog

Feature sizes

  • featureImgSizeLevel
  • searchRegionPaddingScale

Linux下的Docker更改image儲存位置

官方方法

修改/etc/docker/daemon.json加入下面設定

1
2
3
{
"data-root": "/mnt/docker-data"
}

重新啟動docker service

1
2
sudo systemctl stop docker.service
sudo systemctl start docker.service

確認路徑已經修改

1
docker info | grep "Docker Root Dir"

https://docs.docker.com/config/daemon/#daemon-data-directory

步驟參考(Docker更新後原本設定的位置會被改回去,不推薦)

https://linuxconfig.org/how-to-move-docker-s-default-var-lib-docker-to-another-directory-on-ubuntu-debian-linux

Linux使用SOCKS5-proxy上網

簡介

這裡將介紹一個只需要使用SSH連線到一台可以連到對網網路的主機,就可以讓防火牆內的主機上網的方法
圖片說明

注意Docker pull沒辦法用proxychains,解法在後面

利用ssh連接到可以上網的機器

首先本機電腦和遠端電腦都必須有ssh server
首先在本機電腦ssh登入到遠端沒有外網的遠端電腦,然後在遠端電腦使用以下指令連回去本機電腦建立一條SOCKS5 proxy的通道。

1
ssh -D 9050 -q -C -N local_username@192.168.50.1

檢查SOCKS是否開通了

https://superuser.com/questions/303251/how-to-check-if-a-socks5-proxy-works

遠端電腦用以下指令可以檢查開通的port

1
netstat -tlnp

假設我們在9050port開SOCKS proxy,如果有成功開啟SOCKS proxy,應該會看到下面這行

1
tcp        0      0 127.0.0.1:9050          0.0.0.0:*               LISTEN  

安裝Proxychains4

https://blog.csdn.net/leishupei/article/details/120736869
sudo apt-get install proxychains4

在還沒安裝Proxychains4機器上用SOCKS5安裝Proxychains4

1
2
3
 apt -o Acquire::http::Proxy="socks5h://127.0.0.1:9050" -o Acquire::https::Proxy="socks5h://127.0.0.1:9050" update

apt -o Acquire::http::Proxy="socks5h://127.0.0.1:9050" -o Acquire::https::Proxy="socks5h://127.0.0.1:9050" install proxychains4

設定Proxychains4

https://feifei.tw/proxychains4/

sudo nano /etc/proxychains4.conf
proxy_dns 的功能不要關掉

設定檔位置
https://askubuntu.com/a/1477936

手動設定DNS server

export DNS_SERVER=8.8.8.8

https://github.com/rofl0r/proxychains-ng/issues/178#issuecomment-347439800

測試apt指令

proxychains4 sudo apt install zip

docker pull 無法使用proxychains4的解法

1
2
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/proxy.conf
1
2
3
[Service]
Environment="HTTP_PROXY=socks5://127.0.0.1:9050"
Environment="HTTPS_PROXY=socks5://127.0.0.1:9050"
1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

https://docs.docker.com/engine/daemon/proxy/

https://markvanlent.dev/2022/05/10/pulling-docker-images-via-a-socks5-proxy/