Machine Learning in Python (Scikit-learn)-(No.1)配置

1. 閑話篇

機器學習(ML),自然語言處理(NLP),神馬的,最近太火了。。。不知道再過幾年,大家都玩兒ML,還會不會繼續火下去。。。需要有人繼續再添點柴火才行。本人僅僅是一個迷途小書童,知識有限,還望各位ML大神多多指點:)。

最近想系統地收拾一下ML的現有工具,發現比較好的應該是這個http://scikit-learn.org/stable/index.html

對于初學和進階階段的ML研究者們是個不錯的選擇。不過美中不足的是少了Large-scale ML的一些,畢竟這是單機的。后面琢磨琢磨,寫個ADMM(今年ICML劇多相關的論文)的吧,這個之前在MSRA的Learning Group做過一個Turtorial.

尤其是他的參考手冊,更是沒有太多廢話,都能一針見血地講明重點:http://scikit-learn.org/stable/user_guide.html

其實不要指望這個工具包能有啥新的東西,不過就是這些經典的東西,要是你真掌握了,也基本God Like!了。:),特別是你用ML創業的時候,可能真能用上一兩個思路,也就是被訓練出來的思想估計是大學能留下來的,剩下的都在狗肚子里。

我們來大致瀏覽一下這個系統的ML工具的功能,整體內容較多,我們逐步更新,想具體了解哪個部分的童鞋可以留言,我一下子還真很難都詳細介紹(我會基本上保證一周更新一個小章節,逐步學習。首先弄懂模型原理,講出來,然后使用對應數據實戰一下,貼出代碼,作圖,最后利用測試結果適當比較一下模型之間的差異),所有的代碼,我都會后續貼到CSDN或者Github上面。

---------------------------------------------------華麗麗的分割線---------------------------------------------------------

2. 配置篇

推薦學習配置:python 2.7, pycharm IDE (這個Python的IDE不錯,推薦大家用下,如果用過Eclipse寫Java,這個上手會很快), numpy, scipy。其他還有一些需要下載的包,大家可以邊配置邊有問題留言,建議在windows下面弄弄就行,我基本不用Linux。

有些小伙伴建議我也詳細講講在windows下的配置。的確,這一系列的配置還真心沒有那么簡單,我特地找了一臺windows7 Ultimiate SP1 x64 的裸機來重現一下整體配置過程。

首先是Python 2.7 (切記Python 3.x 和2.x的版本完全不是一路貨,不存在3.x向下兼容的問題,所以,如果哪位小伙伴為了追求軟件版本高而不小心安裝了python 3.x,我只能說。。好吧。。你被坑了。最簡單的理解,你可以認為這兩個Python版本壓根就不是一門相同的編程語言,就連print的語法都不同)

1. Python 2.7.x ?在 x64 windows平臺下的解釋器。具體下載地址:https://www.python.org/download/releases/2.7.8/注意64位的是這個Windows X86-64 MSI Installer(2.7.8)

測試這個Python是否在你的環境里配置好,你可以在命令行里直接輸入python,如果報錯,那么你需要手動配置一下環境,這個大家上網搜就可以解決(簡單說,在環境變量PATH里把你的Python的安裝文件夾路徑寫進去)。

2. 然后安裝Pycharm,這個是我在Hulu實習的時候用到過的IDE,還是濤哥推薦的,還不錯。因為有正版收費的問題,推薦大家下載它的(community)版http://www.jetbrains.com/pycharm/download/。安裝好后,它應該會讓你選擇剛才安裝好的Python的解釋器,這樣你就可以做一些簡單的python編程了,用過eclipse的人,這個上手非常快。

3. 接著就需要配置跟sklearn有關的一系列Python的擴展包了。這個美國加州一個學校的一個非官方網站張貼了所有windows直接安裝的版本http://www.lfd.uci.edu/~gohlke/pythonlibs/,特別實用,大家到里面去下載跟python 2.7 amd64有關的安裝包。然后直接下載運行即可。需要下載的一系列擴展包的列表(按照依賴順序):Numpy-MKL, SciPy, Scikit-learn。有了這些就可以學習Scikit-learn這個工具包了。

4. 此外,如果想像我一樣,同時可以畫圖,那么就需要matplotlib,這個也有一個網站手冊http://matplotlib.org/contents.html,同樣也需要一系列擴展包的支持。使用matplotlib 需要如下必備的庫,numpy,dateutil,pytz,pyparsing,six。都能從剛才我推薦的下載網站上獲取到。

上面的一系列都搞定了,大家可以使用我第一個線性回歸的代碼(加粗的代碼)測試一下,直接輸出圖像,最后還能保存成為png格式的圖片。

------------------------------華麗麗的分割線------------------------------------------

3. 數據篇

用工具之前先介紹幾個我會用到的數據

這里大部分的數據都是從這個經典的機器學習網站提供的:

https://archive.ics.uci.edu/ml/

sklearn.datasets里面集成了這個網站里的部分數據(剛接觸Python的童鞋,需要一點點Python的知識,和Java類似,使用現成工具模塊的時候,需要import一下,我們這個基于Python的機器學習工具包的全名是sklearn,這里介紹數據,所以下一個目錄是datasets)。具體的Python代碼:

import sklearn.datasets

數據一:波士頓房價(適合做回歸),以后直接用boston標記

這行代碼就讀進來了

boston = sklearn.datasets.load_boston()

查詢具體數據說明,用這個代碼:

print boston.DESCR

輸出如下:

Data Set Characteristics:

:Number of Instances: 506

:Number of Attributes: 13 numeric/categorical predictive

:Median Value (attribute 14) is usually the target

:Attribute Information (in order):

- CRIM per capita crime rate by town

- ZN proportion of residential land zoned for lots over 25,000 sq.ft.

- INDUS proportion of non-retail business acres per town

- CHAS Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)

- NOX nitric oxides concentration (parts per 10 million)

- RM average number of rooms per dwelling

- AGE proportion of owner-occupied units built prior to 1940

- DIS weighted distances to five Boston employment centres

- RAD index of accessibility to radial highways

- TAX full-value property-tax rate per $10,000

- PTRATIO pupil-teacher ratio by town

- B 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town

- LSTAT % lower status of the population

- MEDV Median value of owner-occupied homes in $1000's

一共506組數據,13維特征,

比如第一個維度的特征是犯罪率,第六個是每個房子平均多少房間等等。

boston.data 獲取這506 * 13的特征數據

boston.target 獲取對應的506 * 1的對應價格

數據二:牽牛花(適合做簡單分類),標記為Iris

import sklearn.datasets

iris = sklearn.datasets.load_iris()

iris.data 獲取特征

iris.target 獲取對應的類別

Data Set Characteristics:

:Number of Instances: 150 (50 in each of three classes)

:Number of Attributes: 4 numeric, predictive attributes and the class

:Attribute Information:

- sepal length in cm

- sepal width in cm

- petal length in cm

- petal width in cm

- class:

- Iris-Setosa

- Iris-Versicolour

- Iris-Virginica

這個數據基本是個ML的入門選手都知道,一共三類牽牛花,獲取特征和對應的類別標簽也是同上

一共150樣本,3類,特征維度為4

數據三: 糖尿病(回歸問題),diabetes

這個數據包很奇怪,沒有描述。我也到原本的UCI的網站上查了一下,也是沒有太好的描述。

import sklearn.datasets

diabetes = sklearn.datasets.load_diabetes()

print diabetes.keys()

這樣的輸出只有data, targets。

我也觀察了一下數據,感覺是經過額外的歸一化處理的,原始的數據樣貌已經看不出來了。。

下面這個圖是我從網站上Copy下來的有限的描述,樣本量為442,特征維度為10,每個特征元素的值都是連續的實數,在正負0.2之間。。目標這個整數值有可能是血糖。

Samples total442

Dimensionality10

Featuresreal, -.2 < x < .2

Targetsinteger 25 - 346

數據四:手寫數字識別(多類分類,10個類別,從0-9)digits

import sklearn.datasets

digits = sklearn.datasets.load_digits()

總體樣本量:1797,每個類別大約180個樣本,每個手寫數字是一個8*8的圖片,每個像素是0-16的整數值。

綜上,大家可以加載相應的數據來玩,這幾個數據算是比較有代表性的。后面會介紹如何利用SKLEARN工具下載更大規模的數據,比如MINIST的大規模的手寫數字識別庫等等。

總之,如果你想獲取特征,就在*.data里,對應的類別或者回歸值在*.target里面

光說不練不行,我對每個介紹的方法都會選用上面的Dataset實際測試一下,并且會酌情給出結果和圖像。

------------------------------華麗麗的分割線------------------------------------------

4.實戰篇

1. Supervised learning

這個監督學習最常用,分類啊,預測回歸(預測個股票啥的,雖然在我大天朝不太適合)啊。

1.1. Generalized Linear Models

最通用的線性模型

把你的特征x和對應的權重w相加,最后爭取接近你的目標y,機器學的就是w。

這個模型應用最廣,其實就是大家會權衡各種各樣的因素,最后給一個總評。

1.1.1. Ordinary Least Squares最小二乘約束

目標函數是這個

要總體的平方和最小。

具體代碼大家import sklearn.linear_model,然后sklearn.linear_model.LinearRegression()就是這個模塊了。做個簡單的什么房價估計還行(別說預測,那個不準,只能說估計一下租房的價格,隨便在搜房網上弄點兒數據,他那里有現成的特征,什么地理位置啊,面積啊,朝向啊等等,最后你回歸一個大致房價玩玩)。

我們就使用波士頓的房價來預測一下(后面的所有python代碼注意縮進!我是沒工夫一行一行調整了。。。多包涵):

'''

Author: Miao Fan

Affiliation: Department of Computer Science and Technology, Tsinghua University, P.R.China.

Email: fanmiao.cslt.thu@gmail.com

'''

import sklearn.datasets

import sklearn.linear_model

import numpy.random

import numpy.linalg

import matplotlib.pyplot

if __name__ == "__main__":

# Load boston dataset

boston = sklearn.datasets.load_boston()

# Split the dataset with sampleRatio

sampleRatio = 0.5

n_samples = len(boston.target)

sampleBoundary = int(n_samples * sampleRatio)

# Shuffle the whole data

shuffleIdx = range(n_samples)

numpy.random.shuffle(shuffleIdx)

# Make the training data

train_features = boston.data[shuffleIdx[:sampleBoundary]]

train_targets = boston.target[shuffleIdx [:sampleBoundary]]

# Make the testing data

test_features = boston.data[shuffleIdx[sampleBoundary:]]

test_targets = boston.target[shuffleIdx[sampleBoundary:]]

# Train

linearRegression = sklearn.linear_model.LinearRegression()

linearRegression.fit(train_features, train_targets)

# Predict

predict_targets = linearRegression.predict(test_features)

# Evaluation

n_test_samples = len(test_targets)

X = range(n_test_samples)

error = numpy.linalg.norm(predict_targets - test_targets, ord = 1) / n_test_samples

print "Ordinary Least Squares (Boston) Error: %.2f" %(error)

# Draw

matplotlib.pyplot.plot(X, predict_targets, 'r--', label = 'Predict Price')

matplotlib.pyplot.plot(X, test_targets, 'g:', label='True Price')

legend = matplotlib.pyplot.legend()

matplotlib.pyplot.title("Ordinary Least Squares (Boston)")

matplotlib.pyplot.ylabel("Price")

matplotlib.pyplot.savefig("Ordinary Least Squares (Boston).png", format='png')

matplotlib.pyplot.show()

結果:

Ordinary Least Squares (Boston) Error:3.35。基本上,平均每筆預測,都會距離真實的價格差3350美金,這個數值的單位是1000 U.S.D. (見數據描述)

下面這個圖就是預測和實際價格的對比圖線,這里是隨機采樣了50%作為訓練,50%做預測,效果還行,看來這個線性模型還可以接受。

1.1.2. Ridge Regression

這個中文一般叫嶺回歸,就是在上面的目標函數上加個正則項,嶺回歸用二范數(L2 norm)。

這個范數的目的在于對整體學習到的權重都控制得比較均衡,因為我們的數據不能保證非常正常,有的時候,接近線性相關的那些噪聲樣本會加劇權重系數的非均衡學習,最后就是這個樣子

一旦某個特征噪音比較大,剛好那個權重也不小,那回歸結果就慘了。

好,我們再用波士頓的房價試試嶺回歸。

'''

Author: Miao Fan

Affiliation: Department of Computer Science and Technology, Tsinghua University, P.R.China.

Email: fanmiao.cslt.thu@gmail.com

'''

import sklearn.datasets

import sklearn.linear_model

import numpy.random

import numpy.linalg

import matplotlib.pyplot

if __name__ == "__main__":

# Load boston dataset

boston = sklearn.datasets.load_boston()

# Split the dataset with sampleRatio

sampleRatio = 0.5

n_samples = len(boston.target)

sampleBoundary = int(n_samples * sampleRatio)

# Shuffle the whole data

shuffleIdx = range(n_samples)

numpy.random.shuffle(shuffleIdx)

# Make the training data

train_features = boston.data[shuffleIdx[:sampleBoundary]]

train_targets = boston.target[shuffleIdx [:sampleBoundary]]

# Make the testing data

test_features = boston.data[shuffleIdx[sampleBoundary:]]

test_targets = boston.target[shuffleIdx[sampleBoundary:]]

# Train with Cross Validation

ridgeRegression = sklearn.linear_model.RidgeCV(alphas=[0.01, 0.05, 0.1, 0.5, 1.0, 10.0])

這個地方使用RidgeCV 直接交叉驗證出我需要試驗的幾個懲罰因子,它會幫我選擇這些里面在集內測試表現最優的一個參數。后面的輸出選擇了0.1。

ridgeRegression.fit(train_features, train_targets)

print "Alpha = ", ridgeRegression.alpha_

# Predict

predict_targets = ridgeRegression.predict(test_features)

# Evaluation

n_test_samples = len(test_targets)

X = range(n_test_samples)

error = numpy.linalg.norm(predict_targets - test_targets, ord = 1) / n_test_samples

print "Ridge Regression (Boston) Error: %.2f" %(error)

# Draw

matplotlib.pyplot.plot(X, predict_targets, 'r--', label = 'Predict Price')

matplotlib.pyplot.plot(X, test_targets, 'g:', label='True Price')

legend = matplotlib.pyplot.legend()

matplotlib.pyplot.title("Ridge Regression (Boston)")

matplotlib.pyplot.ylabel("Price (1000 U.S.D)")

matplotlib.pyplot.savefig("Ridge Regression (Boston).png", format='png')

matplotlib.pyplot.show()

輸出:

Alpha = 0.1

Ridge Regression (Boston) Error: 3.21

基本上,這樣的結果,誤差在3210美金左右,比之前的最一般的線性模型好一點。而且,這種情況下,基本上預測出來的圖線的方差比較小,振幅略小,因為有Ridge的懲罰項的約束,保證每個特征的變化不會對整體預測有過大的影響

1.1.3. Lasso

老是聽MSRA的師兄說這個,貌似還挺火的一個研究,這里面就是把二范數(L2)換成一范數(L1)。

絕對值的這個約束,更想讓學習到的權重稀疏一些,壓縮感知啥的跟這個有關。

這個估計不會有太大的性能提升,對于Boston數據,因為本來特征就不稀疏,后面可以試試newsgroup20。那個夠稀疏。

'''

Author: Miao Fan

Affiliation: Department of Computer Science and Technology, Tsinghua University, P.R.China.

Email: fanmiao.cslt.thu@gmail.com

'''

import sklearn.datasets

import sklearn.linear_model

import numpy.random

import numpy.linalg

import matplotlib.pyplot

if __name__ == "__main__":

# Load boston dataset

boston = sklearn.datasets.load_boston()

# Split the dataset with sampleRatio

sampleRatio = 0.5

n_samples = len(boston.target)

sampleBoundary = int(n_samples * sampleRatio)

# Shuffle the whole data

shuffleIdx = range(n_samples)

numpy.random.shuffle(shuffleIdx)

# Make the training data

train_features = boston.data[shuffleIdx[:sampleBoundary]]

train_targets = boston.target[shuffleIdx [:sampleBoundary]]

# Make the testing data

test_features = boston.data[shuffleIdx[sampleBoundary:]]

test_targets = boston.target[shuffleIdx[sampleBoundary:]]

# Train

lasso = sklearn.linear_model.LassoCV(alphas=[0.01, 0.05, 0.1, 0.5, 1.0, 10.0])

lasso.fit(train_features, train_targets)

print "Alpha = ", lasso.alpha_

# Predict

predict_targets = lasso.predict(test_features)

# Evaluation

n_test_samples = len(test_targets)

X = range(n_test_samples)

error = numpy.linalg.norm(predict_targets - test_targets, ord = 1) / n_test_samples

print "Lasso (Boston) Error: %.2f" %(error)

# Draw

matplotlib.pyplot.plot(X, predict_targets, 'r--', label = 'Predict Price')

matplotlib.pyplot.plot(X, test_targets, 'g:', label='True Price')

legend = matplotlib.pyplot.legend()

matplotlib.pyplot.title("Lasso (Boston)")

matplotlib.pyplot.ylabel("Price (1000 U.S.D)")

matplotlib.pyplot.savefig("Lasso (Boston).png", format='png')

matplotlib.pyplot.show()

輸出:

Alpha = 0.01

Lasso (Boston) Error: 3.39

這個結果的振幅還是比較大的。特別是對于低價位的振幅。

1.1.4. Elastic Net

這個不知道中文怎么說合適,其實就是兼顧了上面兩個正則項(L1和L2兩個先驗(Prior)),既保證能夠訓練出一組比較稀疏的模型(Lasso的貢獻),同時還能兼具嶺回歸L2的好處。這個我沒試過,不知道啥樣的數據這么做最合適,回頭我試幾個數據集,比較一下普通的線性回歸和這個模型的性能。

很自然地,要用一個額外的參數來平衡這兩個先驗約束,一個是懲罰因子alpha,這個之前也有,另一個就是

。這些參數都可以用交叉驗證CV來搞定(每個線性模型都有相應的CV方法,比如ElasticNetCV就是用來干這個的,其實這種CV方法就是模型選擇的范疇了,因為每個不同的額外參數,不是你要學習的W。比如懲罰因子,平衡因子等等,這些構成了不同的數學模型,CV的目標就是來選擇合適的模型,然后再去學習W)。這把來個大鍋燴,兩種范數都用上了:

'''

Author: Miao Fan

Affiliation: Department of Computer Science and Technology, Tsinghua University, P.R.China.

Email: fanmiao.cslt.thu@gmail.com

'''

import sklearn.datasets

import sklearn.linear_model

import numpy.random

import numpy.linalg

import matplotlib.pyplot

if __name__ == "__main__":

# Load boston dataset

boston = sklearn.datasets.load_boston()

# Split the dataset with sampleRatio

sampleRatio = 0.5

n_samples = len(boston.target)

sampleBoundary = int(n_samples * sampleRatio)

# Shuffle the whole data

shuffleIdx = range(n_samples)

numpy.random.shuffle(shuffleIdx)

# Make the training data

train_features = boston.data[shuffleIdx[:sampleBoundary]]

train_targets = boston.target[shuffleIdx [:sampleBoundary]]

# Make the testing data

test_features = boston.data[shuffleIdx[sampleBoundary:]]

test_targets = boston.target[shuffleIdx[sampleBoundary:]]

# Train

elasticNet = sklearn.linear_model.ElasticNetCV(alphas=[0.01, 0.05, 0.1, 0.5, 1.0, 10.0], l1_ratio=[0.1,0.3,0.5,0.7,0.9])

elasticNet.fit(train_features, train_targets)

print "Alpha = ", elasticNet.alpha_

print "L1 Ratio = ", elasticNet.l1_ratio_

# Predict

predict_targets = elasticNet.predict(test_features)

# Evaluation

n_test_samples = len(test_targets)

X = range(n_test_samples)

error = numpy.linalg.norm(predict_targets - test_targets, ord = 1) / n_test_samples

print "Elastic Net (Boston) Error: %.2f" %(error)

# Draw

matplotlib.pyplot.plot(X, predict_targets, 'r--', label = 'Predict Price')

matplotlib.pyplot.plot(X, test_targets, 'g:', label='True Price')

legend = matplotlib.pyplot.legend()

matplotlib.pyplot.title("Elastic Net (Boston)")

matplotlib.pyplot.ylabel("Price (1000 U.S.D)")

matplotlib.pyplot.savefig("Elastic Net (Boston).png", format='png')

matplotlib.pyplot.show()

輸出:

Alpha = 0.01

L1 Ratio = 0.9

Elastic Net (Boston) Error: 3.14

貌似還是混合所有制比較牛逼!知道這年頭審論文最怕遇到題目里面有啥么?Hybird...,這尼瑪性能不提升都對不起這個單詞。。。

1.1.10. Logistic regression

這里補充一個比較實用的邏輯斯蒂回歸,雖然名字叫這個,但是一般用在分類上。

采用這個函數來表達具體樣本的特征加權組合能夠分到哪個類別上(注:下面的圖片來自博客http://blog.csdn.net/marvin521/article/details/9263483

下面的這個sigmod函數對于z值特別敏感,但是他的優點在于他是連續可導的,這個非常重要,便于我們用梯度法計算W。

事實證明,Logistic Regression做分類非常好用也很易用,據說Goolge對點擊率CTR的預測也會用到這個模型,這個我沒有考證過,只是聽說,不過下面的代碼對Iris的分類結果倒是也能說明這個做分類也是挺好用的(這里強調,我們經常看到Logistic Regression用來做二分類,事實上它可以拓展到對多類分類上,我這里不過多介紹,大家可以查Softmax Regression做參考)。

我們使用Iris的數據來測試一下:

大致回顧一下Iris(牽牛花(數據篇有詳細介紹))的數據特點:150個樣本,3類,每類基本50條數據,每個數據條目4中特征,都是連續數值類型。我們的目標就是把隨機抽取的50%(切記要隨機打亂數據,這個數據原始的順序不是打亂的,前50條都是一個類別,別弄錯了。)的數據做個類別0,1,2的預測。

'''

Author: Miao Fan

Affiliation: Department of Computer Science and Technology, Tsinghua University, P.R.China.

Email: fanmiao.cslt.thu@gmail.com

'''

import sklearn.datasets

import sklearn.linear_model

import numpy.random

import matplotlib.pyplot

if __name__ == "__main__":

# Load iris dataset

iris = sklearn.datasets.load_iris()

# Split the dataset with sampleRatio

sampleRatio = 0.5

n_samples = len(iris.target)

sampleBoundary = int(n_samples * sampleRatio)

# Shuffle the whole data

shuffleIdx = range(n_samples)

numpy.random.shuffle(shuffleIdx)

# Make the training data

train_features = iris.data[shuffleIdx[:sampleBoundary]]

train_targets = iris.target[shuffleIdx [:sampleBoundary]]

# Make the testing data

test_features = iris.data[shuffleIdx[sampleBoundary:]]

test_targets = iris.target[shuffleIdx[sampleBoundary:]]

# Train

logisticRegression = sklearn.linear_model.LogisticRegression()

logisticRegression.fit(train_features, train_targets)

# Predict

predict_targets = logisticRegression.predict(test_features)

# Evaluation

n_test_samples = len(test_targets)

X = range(n_test_samples)

correctNum = 0

for i in X:

if predict_targets[i] == test_targets[i]:

correctNum += 1

accuracy = correctNum * 1.0 / n_test_samples

print "Logistic Regression (Iris) Accuracy: %.2f" %(accuracy)

# Draw

matplotlib.pyplot.subplot(2, 1, 1)

matplotlib.pyplot.title("Logistic Regression (Iris)")

matplotlib.pyplot.plot(X, predict_targets, 'ro-', label = 'Predict Labels')

matplotlib.pyplot.ylabel("Predict Class")

legend = matplotlib.pyplot.legend()

matplotlib.pyplot.subplot(2, 1, 2)

matplotlib.pyplot.plot(X, test_targets, 'g+-', label='True Labels')

legend = matplotlib.pyplot.legend()

matplotlib.pyplot.ylabel("True Class")

matplotlib.pyplot.savefig("Logistic Regression (Iris).png", format='png')

matplotlib.pyplot.show()

輸出:

Logistic Regression (Iris) Accuracy: 0.95

使用50%作訓練,50%做測試,分類精度可以達到95%。

下面這個圖算是一個直觀的輔助,因為分類精度比較高,所以預測類別和真實類別對應的走勢幾乎相同:

字數要超了,繼續讀,可以點擊,進入No.2:

http://blog.renren.com/blog/bp/Q7Vlj0xW7D

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容