Docker build的時候遇到Hash Sum mismatch
解法:
在dockerfile apt-get update之前建立一個/etc/apt/apt.conf.d/99fixbadproxy 文件如下
1 | RUN echo "Acquire::http::Pipeline-Depth 0;" >> /etc/apt/apt.conf.d/99fixbadproxy |
解法:
在dockerfile apt-get update之前建立一個/etc/apt/apt.conf.d/99fixbadproxy 文件如下
1 | RUN echo "Acquire::http::Pipeline-Depth 0;" >> /etc/apt/apt.conf.d/99fixbadproxy |
https://haway.30cm.gg/ssl-key-csr-crt-pem/
https://docs.gandi.net/zh-hant/ssl/common_operations/csr.html
憑證可能有不同格式,可以用這兩種指令查看
1 | openssl x509 -inform pem -noout -text -in 'cerfile.cer'; |
1 | openssl x509 -inform der -noout -text -in 'cerfile.cer'; |
https://serverfault.com/a/215617
不同格式的憑證可以互相轉換格式,例如降二進位DER格式轉換成PEM格式可以用下面兩個指令達成
1 | openssl x509 -inform DER -in <path-to-cer-file> -out <path-to-crt-file> |
範例:
1 | openssl x509 -inform DER -in C:\Certificates\AnyCert.cer -out C:\Certificates\AnyCertCrt.crt openssl x509 -in C:\Certificates\AnyCertCrt.crt -out C:\Certificates\AnyCertInPem.pem -outform PEM |
編碼格式說明與憑證講解:
https://blog.miniasp.com/post/2018/04/21/PKI-Digital-Certificate-Format-Convertion-Notes
https://sourceware.org/gdb/wiki/STLSupport
source {full_path}stl-views-1.0.3.gdb
You can also put the command source stl-views-1.0.3.gdb in ~/.gdbinit - and then you’ll have it automatically every time you launch gdb.
Square Brackets [ ]
Angle Brackets < >
https://stackoverflow.com/a/23242584/22299707
1 | cmake -S . -B build |
https://cmake.org/cmake/help/latest/module/FindPkgConfig.html
https://stackoverflow.com/questions/42634710/how-to-use-pkg-config-in-cmake-juci
https://blog.csdn.net/zhizhengguan/article/details/111826697
1 | set(CMAKE_BUILD_TYPE Debug) |
Declare CUDA as a LANGUAGE in your project
1 | project(GTC LANGUAGES CUDA CXX) |
1 | find_package(CUDAToolkit) |
https://cliutils.gitlab.io/modern-cmake/chapters/packages/CUDA.html
1 | SET(OBJS |
https://stackoverflow.com/a/38610428
1 | ExternalProject_Add(Qt |
https://stackoverflow.com/a/3493578/22299707
以下指令可以在外部資料夾下make指令
1 | include(ExternalProject) |
接下來可以用以下指令找出所有編譯好的.o檔並且加到專案內
1 | file(GLOB_RECURSE DEEPSTREAMAPPCONFIGPARSER "/opt/nvidia/deepstream/deepstream/sources/apps/apps-common/src/deepstream-yaml/*.o") |
參考:https://discourse.cmake.org/t/external-project-using-makefile/2692/5
https://cmake.org/cmake/help/latest/guide/tutorial/Adding%20a%20Library.html#step-2-adding-a-librarytarget_include_directories()
和 target_link_libraries()
C++ 、CMake 、CMake Tools
而launch.json可以呼叫task.json裡面的工作,只需要在preLaunchTask中指定,範例如下
1 | { |
https://code.visualstudio.com/docs/cpp/CMake-linux
https://github.com/microsoft/vscode-cmake-tools/tree/024d14df75d397a682da4a864eac420824e4ddba/docs
$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}$
現代的深度學習都有提供automatic differentiation(autograd)和backpropagation的功能,下面將介紹如何使用
為什麼要先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代表座標方向反轉
微分不應該單純的想成斜率,積分也不應該單純想成面積。而是用另以種角度來想,微分代表在某一個點上,把它放很大來看之後他的線性映射是如何(linear map)
對二維函式來說,Jacobian matrix就是在(a, b)附近的linear map
https://youtu.be/wCZ1VEmVjVo
https://youtu.be/CfW845LNObM
https://angeloyeo.github.io/2020/07/24/Jacobian_en.html
經過Cross-Correlation Operation後,輸出的tensor尺寸為
$(n_h − k_h + 1 ) × (n_w − k_w + 1 )$
Convolutional Layers就是經過Cross-Correlation Operation之後的tensor對每一個element都加上一個bias。Conv的kernel如同前面MLP的權重,初始化的時候我們是用亂數初始化
已知一個人工製作的邊緣偵測器kernel為[1, -1]可以偵測垂直線,等一下會嘗試讓電腦自己學習出這個kernel
接下來我們要常識讓電腦自動學習kernel,為了簡單起見bias為0。(為什麼先sum再backward?)(https://www.youtube.com/watch?v=Q7KekwUricc)(https://dlvu.github.io/)
以下程式可以自動學習kernel
1 | # Construct a two-dimensional convolutional layer with 1 output channel and a |
可以避免寫死in_features,對於動態的in_features來說比較方便
https://jarvislabs.ai/blogs/PyTorch-lazy-modules/
再Deeplearning 使用的Convolution計算方式其實真正的名稱是Cross-Correlation,但是由於Cross-Correlation和Convolution的差別只在於kernerl上下和左右都互換,但對於traing的結果影響不大,所以依然稱為Convolution
convolutional layer的輸出稱為Feature Map。
receptive field則是影響輸出結果之前的所有元素。()
單純使用conv會使得後面layer的input快速變小。Padding可以解決這個狀況,相反的Stride則是用來快速讓輸入變小。
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數量有一邊會多一個。
當高的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$$
當輸入的channel數量大於1的時候,每一個channel會需要至少一個kernel。假設現在輸入有3個channel,每一個channel分別對一個kernel之後相加結果就是輸出。
1 | def corr2d_multi_in(X, K): |
為了讓神經網路學到更多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 | def corr2d_multi_in_out(X, K): |
1 × 1 Convolutional Layer唯一能夠影像的就是channel這個維度。
1 × 1 Convolutional Layer可以視為在每一個單獨的像素位置上的channel的fully conection layer。並且將channel數由$c_i$轉換成$c_o$。
Pooling讓我們可以專注在影像比較大的區域而不會因為影像中細微的變化就影響輸出結果。
池化層沒有任何參數,將window所有元素平均為Average Pooling,取出最大值則為Maximum Pooling。
跟conv layer一樣pooling也有padding和Stride,而由於pooling主要目的是從一個區域擷取資訊,所以深度學習框架預設將Stride的大小設定跟pooling window一樣大,不過我們還是可以自行修改大小。
不同於conv layer,pooling對每一個channel是分開處理的,而不像conv layer會把其他channel的結果相加。因此pooling的輸入和輸出channel數目是一樣的。
接下來介紹LeNet
下面是LeNet的Pytorch實作,這裡使用了Xavier initialization
1 | def init_cnn(module): #@save |
雖然CNN的參數比較少,但是計算量並不少,用GPU計算更適合。
硬體運算速度的提升增加了CNN可行性。
有一派的研究員相信圖片中的feature是可以讓電腦自己學習得到的。AlexNet在地層layer所學到的kernel跟傳統機器學習手工製作的feature很像。
推動CNN兩大因素別是”資料”和”電腦硬體”,過去的資料集都很小而且電腦運算速度很慢。
AlexNet把sigmoid activation function改成ReLU activtion function。
ReLU activation function,讓模型訓練變得更加容易。因為如果初始化的參數很接近0或1,sigmoid activation function的輸出值會很接近0,這使的反向傳播幾乎沒有作用。而ReLU activation function卻不會有這種情形。
AlexNet在訓練的時候也大量的利用圖片擴增的技巧,這使得訓練結果不會overfitting。
AlexNet弱點是他最後的兩個layer佔用了非常大量的記憶體和運算,這對需要高速運算的場景非常不利。
另外一個可以注意的點是即使AlexNet的參數量遠大於資料及照片總數,AlexNet也幾乎沒有Overfitting。這是因為有用到現代化的正規化方法如Dropout。
神經網絡架構的設計日益抽象化,研究人員從思考個別神經元逐漸轉向整個層面,然後到塊狀結構,即層的重複模式。十年後,這已經進一步發展到研究人員使用完整的訓練模型,將它們重新應用於不同但相關的任務。這種大型 pretrained models通常被稱為foundation models 。
這種Blocks的概念最早出現在VGG network,使用迴圈和子程序,可以在任何現代深度學習框架中輕鬆地在代碼中實現這些重複的結構。
CNNs的基本構建塊是以下順序的序列:(i)帶填充的卷積層以保持解析度,(ii)如ReLU之類的非線性激活函數,(iii)如最大池化之類的池化層以減小解析度。這種方法的一個問題是空間解析度下降得相當迅速。特別是,這在所有維度(d)用完之前,對於網絡在卷積層上存在著 $log_2d$的硬限制。例如,在ImageNet的情況下,以這種方式不可能有超過8個卷積層。
Simonyan和Zisserman(2014)的關鍵想法是使用連續的小卷層來取代一個大卷積層,例如兩個3x3的卷積層其實涵蓋的像素跟一個5x5一樣大,經過觀察利用連續小的卷積層的效果跟直接用一個大卷積層的效果相似。
堆疊3×3的卷積後來成為後來的深度網絡的黃金標準,直到最近由Liu等人(2022)重新審查了這個設計決策。
1 | def vgg_block(num_convs, out_channels): |
VGG Network可以拆成兩個部分,前半段是多個conv layer和pooling layer組成,後半段是fully connected layer所組成。
下面程式定義了VGG network,在這裡我們利用for迴圈將多個VGG block組合成VGG netowrk
1 | class VGG(d2l.Classifier): |
VGG, LeNet, VGG都有一個共通的缺點。
由一個conv layer加上兩個$1 \times 1$ conv layer組成
GoogLeNet可以說是第一個將模型拆解成三個部位stem, body, head的模型。
Inception Blocks跟前面模型不一樣的是一個block有三個分支
Batch Normalization是一個能夠有效加速深度模型收斂的方法,與 residual blocks可以讓模型深度達到一百層。
Batch normalization 可以被套用在單一個layer或是所有layer,如此一來對每一層的input都先做一次 normalization(也就是平均為0標準差為1)。
注意到如果我們套用batch normalization在minibatches為一的狀況下,我們不會學到任何東西,因為所有的 hidden unit都會變成0。
因此使用Batch normalization的時候batch size的大小變成十分重要。
Batch normalization中scale parameter 和shift parameter是透過模型學習而得。
在這之前我們的資料長度都是固定的,在這章將要學習如何讓模型學習長度不固定的序列資料。
RNN利用recurrent connection讓模型可以學習序列化的資料,可以把他想成一個迴授迴路
9.1 序列化資料
在此之前我們的特徵向量長度是固定的,而序列化資料的特徵向量是以時間排序而且長度不固定的資料。
1 | git rev-list --objects --all | |
https://stackoverflow.com/questions/10622179/how-to-find-identify-large-commits-in-git-history
https://rtyley.github.io/bfg-repo-cleaner/
1 | --no-blob-protection |
https://stackoverflow.com/a/47421624
輸入: 一個沒有規則的數字陣列。
輸出: 排序好的數字陣列
MergeSort是divide-and-conquer方法的展現。recursive divide-and-conquer algorithm是不斷的利用遞迴把問題拆變小,直到問題不能在拆分後再把結果合併回來。對於array把問題變小的方式最直覺就是把array拆兩半,拆到最後再慢慢把array合併回來。
下面是MergeSort的Pseudocode
1 | Input: array A of n distinct integers. |
這段程式碼有幾點可以注意
在上面的Pseudocode還有一個Merge函式,下面是關於Merge函式的Pseudocode。想法是將
排序好的C和D array依序再排序成新的排序過的array。
1 | Merge |
計算演算法速度的方式就是計算這個演算法總共需要執行多少次最基礎的計算。也就是計算Running Time
Lemma 1.1 (Running Time of Merge)
對於合併兩個長度為$l/2$的array的,合併最多只會用到6l
步(可從前面Merge的Pseudocode推論。)
Theorem 1.2 (Running Time of MergeSort)
: 對於每個長度$n\geq 1$的array, MergeSort的Merge步驟最多執行$6nlog_2n+6n$個步驟
從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$
我們在計算演算法速度的時候會有三個原則,以下逐一介紹。因為在有些時候為了簡化計算,我們會做出一些取設,但是為了讓數學分析依然能夠預測演算法的速度,因此我們才用到下面三個原則。
在前面推導的結果$6nlog_2n+6n$是不管n是什麼數字這個公式都能夠使用。也就是我們沒有對n做任何的假設。這種分析法稱為Worst-Case Analysis,因為這個演算法最慢的計算時間依然符合這個公式。
除了Worst-Case Analysis之外還有average-case analysis和 analysis of benchmark instances可以使用,但是這兩個使用的時候必須非常清楚所處理的問題的條件。
在運行時間界限中,我們不應過於關注小的常數因子或低階項。
Algorithms Illuminated