机器学习 | 模型选择
文章目錄
- 1. 模型驗證
- 1.1 錯誤的模型驗證方法
- 1.2 正確的模型驗證方法
- 1.2.1 留出集
- 1.2.2 交叉驗證
- 1.2.3 K折交叉驗證
- 1.2.4 留一法 LOO
- 2. 偏差-方差
- 2.1 泛化誤差、偏差及方差
- 2.2 泛化誤差與偏差及方差的關系
- 3. 擬合程度
- 3.1 欠擬合與過擬合
- 3.2 影響擬合程度的因素
- 3.3 模型復雜度
- 2.3.1 模型復雜度圖表
- 3.3.2 驗證曲線
- 3.3.2.1 Sklearn 驗證曲線
- 3.4 訓練集規模
- 3.4.1 學習曲線
- 3.4.1.1 學習曲線與擬合程度
- 3.4.1.2 Sklean 學習曲線
- 參考資料
相關文章:
機器學習 | 目錄
1. 模型驗證
模型驗證:(Model Validation)就是在選擇模型和超參數之后,通過對訓練數據進行學習,對比已知數據的預測值與實際值的差異。[1]
我們將首先通過一個簡單方法實現模型驗證,并告訴你為什么那樣做行不通。之后,介紹如何用留出集(Holdout Set)與交叉驗證(Cross-Validation)實現更可靠的模型驗證。
1.1 錯誤的模型驗證方法
我們將使用鳶尾花數據來演示一個簡單的模型驗證方法。
首先加載數據:
from sklearn.datasets import load_irisiris = load_iris() X = iris.data y = iris.target然后選擇模型和超參數。這里使用一個kkk近鄰分類器,超參數為 n_neighbors=1。這是一個非常簡單直觀的模型,新數據的標簽與其最接近的訓練數據的標簽相同:
from sklearn.neighbors import KNeighborsClassifier model = KNeighborsClassifier(n_neighbors=1)然后訓練模型,并由他來預測已知標簽的數據:
model.fit(X,y) y_model = model.predict(X)最后,計算模型的準確率:
from sklearn.metrics import accuracy_score accuracy_score(y, y_model) 1.0準確得分是1.0,也就是模型識別標簽的正確率是 100% !但是這樣測量的準確率可靠嗎?我們真的有一個在任何時候準確率都是 100% 的模型嗎?
你可能已經猜到了,答案是否定的。其實這個方法又個根本缺陷:它用同一套數據訓練和評估模型。另外,最近鄰模型是一種與距離相關的評估器,只會簡單地存儲訓練數據,然后把新數據與儲存的已知數據進行對比來預測標簽。在理想情況下,模型的準確率總是 100% 。
1.2 正確的模型驗證方法
1.2.1 留出集
那么怎么樣才能驗證模型呢?其實留出集(Holdout Set)可以更好地評估模型性能,也就是說,將數據分為兩部分,一部分用來訓練模型,另一部分用來驗證模型性能。在Sklearn里用train_test_split(Sklearn官方文檔)工具就可以實現:
from sklearn.datasets import load_irisiris = load_iris() train_data = iris.data test_data = iris.target# 一半數據做為訓練集,一半數據作為測試集(默認test_size = 0.75) from sklearn.model_selection import train_test_splitX1, X2, y1, y2 = train_test_split(X, y, random_state=0, train_size=0.5)# 用模型擬合訓練數據 model.fit(X1, y1)# 利用訓練好的模型進行預測 y2_model = model.predict(X2)# 在測試集中評估模型準確率 from sklearn.metrics import accuracy_scoreaccuracy_score(y2, y2_model) 0.9066666666666666這樣就可以獲得更合理的結果了:最近鄰分類器在這份留出集上的準確率是 90.67%。 這里的留出集類似于新數據,因為模型之前沒有“接觸”過它們。
1.2.2 交叉驗證
用留出集進行模型驗證有一個缺點,就是模型失去了一部分訓練機會,在上面的模型里,有一半的數據沒有為模型訓練作出貢獻。這顯然不是最優解,而且可能還會出現問題——尤其是在數據集規模比較小的時候。
解決這個問題的方法是交叉驗證(Cross Validation),也就是做一組擬合,讓數據的每個子集即是訓練集,又是驗證集。如下圖所示,對數據進行兩輪交叉驗證:
這里進行了兩輪驗證實驗,輪流用一組數據作為留出集。所以我們可以對1.2.1的例子實現交叉驗證:
y2_model = model.fit(X1, y1).predict(X2) y1_model = model.fit(X2, y2).predict(X1) accuracy_score(y1, y1_model), accuracy_score(y2, y2_model) (0.96, 0.9066666666666666)這樣就得到了兩個準確率,將二者結合(例如求均值)獲得一個更為準確的模型總性能,這種形式的交叉驗證被稱為兩輪交叉驗證——將數據集分為兩個子集,依次將每個子集作為驗證集。
因此,也可以利用Sklearn的交叉驗證函數cross_val_score(Sklearn官方文檔)實現兩輪交叉驗證
from sklearn.model_selection import cross_val_scorecross_val_score(model, X, y, cv=2) array([0.94666667, 0.94666667])交叉驗證不是一種構建可以應用于新數據的模型的方法:交叉驗證不會返回一個模型。在調用cross_val_score時,內部會構建多個模型,但交叉驗證的目的只是評估給定算法在特定數據集上訓練后的泛化性能的好壞。(若采用交叉驗證來評價模型,則可以利用所有樣本來訓練模型,而無需單獨留出驗證集)
1.2.3 K折交叉驗證
K折交叉驗證(KFold Cross Validation)及在交叉驗證的基礎上,將數據劃分為kkk組(cv=k),其中k?1k-1k?1組進行訓練,剩下1組作為驗證集,重復進行kkk次,如下圖所示:
我們可以將1.2.2的數據分為5組,進行5輪交叉檢驗:
from sklearn.model_selection import cross_val_scorecross_val_score(model, X, y, cv=5) array([0.96666667, 0.96666667, 0.93333333, 0.93333333, 1. ])1.2.4 留一法 LOO
當我們交叉驗證的輪數與樣本數相同時,即每次只有一個樣本做測試,其他樣本全用于訓練,這種交叉驗證類型被稱為留一法(LOO, Leave One Out),如下所示:
from sklearn.model_selection import LeaveOneOut scores = cross_val_score(model, X, y, cv=LeaveOneOut()) print(scores) scores.mean() [1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1.0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0.1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 0. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.1. 1. 1. 1. 1. 1.]0.962. 偏差-方差
2.1 泛化誤差、偏差及方差
泛化誤差的含義,其實就是想看看訓練后的模型是否具有代表性。我們使用偏差(bias)和方差(variance)來描述。
偏差是什么?給定了一些新的樣本,我們使用訓練好的模型對這個新樣本進行估值,這個估值與真實值的差距就是偏差。
方差是什么?在不同的訓練集上,即使是同一模型我們可能也會得到不同的參數,那么不同訓練集上得到的假設函數對新樣本的所做出的估值就是不同的。我們用這些不同估值的期望作為最終這個模型對新樣本的估值,而方差就是估值與不同訓練集得到的估值之間的離散程度。
這和我們統計上的期望與方差是類似的,可以對比來看。我們希望最終的估值與實際值相差不大,而且得到的模型也要相對穩定,這是就可以說該模型的通用型比較強,也就是泛化。[2]
2.2 泛化誤差與偏差及方差的關系
我們剛才說偏差和方差可以衡量一個模型是否具有代表性,那么當我們在驗證集上得到了泛化誤差后怎么評價這個模型呢?我們來看一下泛化誤差的構成:
泛化誤差=偏差2+方差+噪聲2泛化誤差=偏差^2+方差+噪聲^2泛化誤差=偏差2+方差+噪聲2
為了推導出這個公式,首先定義幾個概念:
在訓練集ddd上,我們訓練后的模型為fd(x)f_d(x)fd?(x)
該模型對數據xxx的預測輸出:f(x)ˉ=Ed[fd(x)]\bar{f(x)}=E_d[f_d(x)]f(x)ˉ?=Ed?[fd?(x)]
驗證集樣本的真實值:yyy,標簽值:ydy_dyd?
噪聲為樣本的標簽值與真實值的出入:ε=y?yd\varepsilon=y-y_dε=y?yd?,服從均值為0的高斯分布 ε~N(0,σ2)\varepsilon \thicksim N(0, \sigma^2)ε~N(0,σ2)
偏差為預測輸出與樣本標簽的差值:bias=y?f(x)ˉbias=y-\bar{f(x)}bias=y?f(x)ˉ?
方差為預測輸出與不同測試集差的離散程度:var=Ed[(fd(x)?f(x)ˉ)2]var=E_d[(f_d(x)-\bar{f(x)})^2]var=Ed?[(fd?(x)?f(x)ˉ?)2]
泛化誤差:Ed[(yd?fd(x))2]E_d[(y_d-f_d(x))^2]Ed?[(yd??fd?(x))2]
泛化誤差即每一組訓練集得到結果后與驗證集計算誤差,誤差的均值就作為衡量泛化的標準。
Ed[(yd?fd(x))2]=Ed[(yd?f(x)ˉ+f(x)ˉ?fd(x))2]=Ed[(yd?f(x)ˉ)2]+Ed[(f(x)ˉ?fd(x))2]+2?E[(yd?f(x)ˉ)(f(x)ˉ?fd(x))]=Ed[(yd?f(x)ˉ)2]+Ed[(f(x)ˉ?fd(x))2]+2?0?E[(yd?f(x)ˉ)]=Ed[(yd?f(x)ˉ)2]+Ed[(f(x)ˉ?fd(x))2]=Ed[(yd?y+y?f(x)ˉ)2]+Ed[(f(x)?fd(x)ˉ)2]=Ed[(yd?y)2]+Ed[(y?f(x)ˉ)2]+2?0?Ed[y?f(x)ˉ]+Ed[(f(x)?fd(x)ˉ)2]=Ed[(yd?y)2]+Ed[(y?f(x)ˉ)2]+Ed[(f(x)?fd(x)ˉ)2]=ε2+bias2+var\begin{aligned} E_d[(y_d-f_d(x))^2] & = E_d[(y_d-\bar{f(x)}+\bar{f(x)}-f_d(x))^2] \\ & = E_d[(y_d-\bar{f(x)})^2]+E_d[(\bar{f(x)}-f_d(x))^2]+2\cdot E[(y_d-\bar{f(x)})(\bar{f(x)}-f_d(x))] \\ & = E_d[(y_d-\bar{f(x)})^2]+E_d[(\bar{f(x)}-f_d(x))^2]+2\cdot 0 \cdot E[(y_d-\bar{f(x)})] \\ & = E_d[(y_d-\bar{f(x)})^2]+E_d[(\bar{f(x)}-f_d(x))^2]\\ & = E_d[(y_d-y+y-\bar{f(x)})^2]+E_d[(f(x)-\bar{f_d(x)})^2]\\ & = E_d[(y_d-y)^2]+E_d[(y-\bar{f(x)})^2]+2\cdot 0\cdot E_d[y-\bar{f(x)}]+E_d[(f(x)-\bar{f_d(x)})^2]\\ & = E_d[(y_d-y)^2]+E_d[(y-\bar{f(x)})^2]+E_d[(f(x)-\bar{f_d(x)})^2]\\ & = \varepsilon^2+bias^2+var \end{aligned} Ed?[(yd??fd?(x))2]?=Ed?[(yd??f(x)ˉ?+f(x)ˉ??fd?(x))2]=Ed?[(yd??f(x)ˉ?)2]+Ed?[(f(x)ˉ??fd?(x))2]+2?E[(yd??f(x)ˉ?)(f(x)ˉ??fd?(x))]=Ed?[(yd??f(x)ˉ?)2]+Ed?[(f(x)ˉ??fd?(x))2]+2?0?E[(yd??f(x)ˉ?)]=Ed?[(yd??f(x)ˉ?)2]+Ed?[(f(x)ˉ??fd?(x))2]=Ed?[(yd??y+y?f(x)ˉ?)2]+Ed?[(f(x)?fd?(x)ˉ?)2]=Ed?[(yd??y)2]+Ed?[(y?f(x)ˉ?)2]+2?0?Ed?[y?f(x)ˉ?]+Ed?[(f(x)?fd?(x)ˉ?)2]=Ed?[(yd??y)2]+Ed?[(y?f(x)ˉ?)2]+Ed?[(f(x)?fd?(x)ˉ?)2]=ε2+bias2+var?
由推導可知,對于每一次交叉驗證我們可以得到一組誤差(yd?fd(x))2(y_d-f_d(x))^2(yd??fd?(x))2,當我們把所有誤差求均值后得到泛化誤差,泛化誤差又可以分解為偏差、方差以及噪聲:
(1)Ed[(yd?fd(x))2]=bias2+var+ε2E_d[(y_d-f_d(x))^2] = bias^2+var+\varepsilon^2\tag{1}Ed?[(yd??fd?(x))2]=bias2+var+ε2(1)
3. 擬合程度
3.1 欠擬合與過擬合
欠擬合\高偏差:(Underfitting\High Bias)指過度簡化了模型,而無法捕捉到數據的復雜度。表現為在訓練集和測試集上正確率都很低。
過擬合\高方差:(Overfitting\High Variance)指過度復雜化了模型,因此這種學習器偏向于記住數據,而不是學習數據,這樣就會導致泛化性能下降。表現為在訓練集上表現很好(甚至在訓練集上準確率到達100%),而在測試集上正確率卻很低。
舉個例子,對以下數據,分別使用一次線性模型,二次方程模型和六次多項式模型進行擬合,可以看到:
對于一次模型,它對與數據的擬合程度并不好,因此在測試集上表現不好;
對于六次模型,它記住了數據,但未能找到訓練集的良好屬性,以很好的泛化到測試劑,所以即使在訓練集上表現很好,在測試集上也會表現很差;
而對于二次模型,它很好的學習到了訓練集的特點,因此在訓練集和測試集上表現都會不錯。
3.2 影響擬合程度的因素
影響擬合程度的因素有很多,如模型的復雜程度和訓練集規模。
對于一定數量的訓練樣本,簡單的模型容易造成欠擬合,而太過復雜的模型容易造成過擬合;
對于一定復雜度的模型,過少的訓練樣本容易導致模型過擬合,過多的訓練樣本容易導致過擬合。
3.3 模型復雜度
模型復雜度與擬合程度的關系:[3]
我們可以利用公式(1)對不同模型復雜度下的泛化誤差進行拆解,可以得到下面的圖像,左側代表了欠擬合,右側代表了過擬合,可以看到左側呈現出了高偏差的特點,而右側呈現出了高方差的特點。
2.3.1 模型復雜度圖表
x 軸:模型復雜度
y 軸:預測誤差
圖的左側為一個欠擬合(高偏差)模型,右側為一個過擬合(高誤差)模型,
舉個例子:對以下數據,分別使用一次線性模型,二次方程模型和六次多項式模型進行分類,我們將數據分為訓練集和測試集,利用訓練集進行訓練,之后利用訓練集和測試集進行預測并計算誤差。我們將誤差畫在下面的表上,就得到了模型復雜度圖表。
左側是一個欠擬合模型,它具有較高的訓練誤差和測試誤差;
右側是一個過擬合模型,它具有較低的訓練誤差和較高的測試誤差;
中間的一個較好的模型,它具有相對較低的訓練和測試誤差。
3.3.2 驗證曲線
對之前的回歸模型,我們利用回歸指標R2R^2R2(機器學習 | 分類評估指標)分別計算不同復雜度下的訓練得分和驗證得分:
將模型復雜度圖標的 y 軸指標由誤差改為模型得分,就得到了驗證曲線。相同的,在驗證曲線的左側為欠擬合,在驗證曲線的右側為過擬合。
3.3.2.1 Sklearn 驗證曲線
用交叉驗證計算一個多項式回歸模型,其多項式的次數是一個可調參數。在 Sklern 中,可以用一個帶多項式的預處理器的簡單線性回歸模型實現。我們將用一個管道命令來組合這兩種操作:
from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.pipeline import make_pipelinedef PolynomialRegression(degree=2, **kwargs):return make_pipeline(PolynomialFeatures(degree),LinearRegression(**kwargs))現在來創造一些數據給模型擬合:
import numpy as npdef make_data(N, err=1.0, rseed=1):# randomly sample the datarng = np.random.RandomState(rseed)X = rng.rand(N, 1) ** 2y = 10 - 1. / (X.ravel() + 0.1)if err > 0:y += err * rng.randn(N)return X, yX, y = make_data(40)通過數據可視化,將不同次數的多項式擬合曲線畫出來:
%matplotlib inline import matplotlib.pyplot as plt import seaborn; seaborn.set() # plot formattingX_test = np.linspace(-0.1, 1.1, 500)[:, None]plt.scatter(X.ravel(), y, color='black') axis = plt.axis() for degree in [1, 3, 5]:y_test = PolynomialRegression(degree).fit(X, y).predict(X_test)plt.plot(X_test.ravel(), y_test, label='degree={0}'.format(degree)) plt.xlim(-0.1, 1.0) plt.ylim(-2, 12) plt.legend(loc='best');通過validation_curve函數(Sklearn 官方文檔)畫驗證曲線,只需要提供模型、數據、參數名稱和驗證范圍學習,函數就會自動計算驗證范圍內的訓練得分和驗證的分:
from sklearn.model_selection import validation_curve degree = np.arange(0, 21) train_score, val_score = validation_curve(PolynomialRegression(), X, y,'polynomialfeatures__degree', degree, cv=7)plt.plot(degree, np.median(train_score, 1), color='blue', label='training score') plt.plot(degree, np.median(val_score, 1), color='red', label='validation score') plt.legend(loc='best') plt.ylim(0, 1) plt.xlabel('degree') plt.ylabel('score');從驗證曲線中可以看出,偏差與方差的均衡性最好的是三次多項式。我們可以計算結果,并將模型畫在原始數據上:
plt.scatter(X.ravel(), y) lim = plt.axis() y_test = PolynomialRegression(3).fit(X, y).predict(X_test) plt.plot(X_test.ravel(), y_test); plt.axis(lim);雖然尋找最優模型并不需要我們計算訓練得分,但是檢查訓練得分與驗證得分之間的關系可以讓我們對模型的性能有更加直觀的認識。
3.4 訓練集規模
影響模型復雜度的一個重要因素就是最優模型往往收到訓練數據量的影響。例如對于3.3.2.1的例子,我們增加5倍數據量。與之前相同,畫出200個樣本點的驗證曲線并疊加到40個樣本點的驗證曲線上,其中虛線為40個樣本點的驗證曲線,實現為200個樣本點的驗證曲線:
從驗證曲線就可以明顯看出,大數據集支持更復雜的模型,雖然得分頂點大概是六次多項式,但是即使到了20次多項式,過擬合的情況也不太嚴重——驗證得分與訓練得分依然十分接近。
通過觀察驗證曲線的變化趨勢,可以發現有兩個影響模型效果的因素:模型復雜度和訓練集規模。
3.4.1 學習曲線
學習曲線(Learning Curve):通常,我們將模型看成是與訓練數據規模相官的函數,通過不斷增大數據集的規模來擬合模型,一次來觀察模型的行為。反映訓練集規模的訓練得分/驗證得分曲線被稱為學習曲線。[4]
通過畫出不同訓練集大小時訓練集和交叉驗證的準確率,可以看到模型在新數據上的表現,進而來判斷模型是否方差偏高或偏差過高,以及增大訓練集是否可以減小過擬合。
學習曲線的特征包含以下三點:
特定復雜度的模型對較少的數據集容易過擬合:此時訓練得分較高,驗證得分較低。
特定復雜度的模型對較大的數據集容易欠擬合:隨著數據的增大,訓練得分會不斷降低,而驗證得分會不斷升高。
模型的驗證集得分永遠不會高于訓練集得分:兩條曲線一直在靠近,但永遠不會交叉。
學習曲線最重要的特征是:隨著訓練樣本數量的增加,兩條得分曲線會收斂到一個定值。因此,一旦數據多到使模型得分已經收斂,**那么增加更多的訓練樣本也無濟于事!**改善模型性能的唯一方法就是換模型(通常是換成更復雜的模型)。
3.4.1.1 學習曲線與擬合程度
欠擬合:當訓練集和測試集的誤差收斂但卻很高時,為高偏差。左上角的偏差很高,訓練集和驗證集的準確率都很低,很可能是欠擬合。我們可以增加模型參數,比如,構建更多的特征,減小正則項。此時通過增加數據量是不起作用的。
過擬合:當訓練集和測試集的誤差之間有大的差距時,為高方差。當訓練集的準確率比其他獨立數據集上的測試結果的準確率要高時,一般都是過擬合。右上角方差很高,訓練集和驗證集的準確率相差太多,應該是過擬合。我們可以增大訓練集,降低模型復雜度,增大正則項,或者通過特征選擇減少特征數。
理想情況是是找到偏差和方差都很小的情況,即收斂且誤差較小。[5]
3.4.1.2 Sklean 學習曲線
下面計算前面數據集的二次多項式模型和九次多項式模型的學習曲線:
import numpy as npdef make_data(N, err=1.0, rseed=1):# randomly sample the datarng = np.random.RandomState(rseed)X = rng.rand(N, 1) ** 2y = 10 - 1. / (X.ravel() + 0.1)if err > 0:y += err * rng.randn(N)return X, yX, y = make_data(40)from sklearn.model_selection import learning_curvefig, ax = plt.subplots(1, 2, figsize=(16, 6)) fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)for i, degree in enumerate([2, 9]):N, train_lc, val_lc = learning_curve(PolynomialRegression(degree),X, y, cv=7,train_sizes=np.linspace(0.3, 1, 25))ax[i].plot(N, np.mean(train_lc, 1), color='blue', label='training score')ax[i].plot(N, np.mean(val_lc, 1), color='red', label='validation score')ax[i].hlines(np.mean([train_lc[-1], val_lc[-1]]), N[0], N[-1],color='gray', linestyle='dashed')ax[i].set_ylim(0, 1)ax[i].set_xlim(N[0], N[-1])ax[i].set_xlabel('training size')ax[i].set_ylabel('score')ax[i].set_title('degree = {0}'.format(degree), size=14)ax[i].legend(loc='best')從圖中可以看出,不同模型復雜度下,要達到收斂所需要的訓練集數據量是不同的;而當學習曲線已經收斂時,**再增加訓練數據也不能顯著改善擬合效果!**這種情況就類似于左圖顯示的二次多項式模型的學習曲線。
提高收斂得分的唯一方法就是換模型(通常也是更復雜的模型)。如右圖所示:采用復雜度更高的模型之后,雖然學習曲線的收斂得分提高了(對比虛線所在位置),但是模型的方差也變大了(對比訓練得分與驗證得分的差異即可看出)。
如果我們為復雜度更高的模型繼續增加訓練數據,那么學習曲線最終也會收斂。
參考資料
[1] Jake VanderPlas, 陶俊杰, 陳小莉. Python 數據科學手冊[M]. 北京: 人民郵電出版社, 2019: 314-317.
[2] Cerisier.談談對泛化誤差的理解[EB/OL].https://blog.csdn.net/Cerisier/article/details/78122653, 2017-09-28.
[3] Scott Fortmann-Roe.Understanding the Bias-Variance Tradeoff[EB/OL].http://scott.fortmann-roe.com/docs/BiasVariance.html, 2012-06.
[4] Jake VanderPlas, 陶俊杰, 陳小莉. Python 數據科學手冊[M]. 北京: 人民郵電出版社, 2019: 322-326.
[5] 不會停的蝸牛.用學習曲線 learning curve 來判別過擬合問題[EB/OL].https://www.jianshu.com/p/d89dee94e247, 2017-06-22.
總結
以上是生活随笔為你收集整理的机器学习 | 模型选择的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [yc]详解link
- 下一篇: C程序的编译过程