Python機器學習筆記:利用Keras進行分類預測


  Keras是一個用於深度學習的Python庫,它包含高效的數值庫Theano和TensorFlow。

  本文的目的是學習如何從csv中加載數據並使其可供Keras使用,如何用神經網絡建立多類分類的數據進行建模,如何使用scikit-learn評估Keras神經網絡模型。

前言,對兩分類和多分類的概念描述

  (前言是整理別人博客的筆記https://blog.csdn.net/qq_22238533/article/details/77774223)

1,在LR(邏輯回歸)中,如何進行多分類?

  一般情況下,我們所認識的lr模型是一個二分類的模型,但是能否用lr進行多分類任務呢?答案當然是可以的。

   不過我們需要注意的是,我們有許多種思想利用lr來進行分類

2,訓練多個二分類器的思想

  既然天然的lr是用來做二分類,那么我們很自然地想到把多分類划分為多個二分類的任務。

  具體有以下三種策略:

2.1 一對一

 假如某個分類中有N個類別,我們將這N個類別進行兩兩配對(兩兩配對后轉化為二分類問題)。那么我們可以得到個二分類器。(簡單解釋一下,相當於在N個類別里面抽2個)

之后,在測試階段,我們把新樣本交給這 個二分類器。於是我們可以得到 個分類結果。把預測的最多的類別作為預測的結果。
下面,我給一個具體的例子來理解一下。
  上圖的意思其實很明顯,首先把類別兩兩組合(6種組合)。組合完之后,其中一個類別作為正類,另一個作為負類(這個正負只是相對而言,目的是轉化為二分類)。然后對每個二分類器進行訓練。可以得到6個二分類器。然后把測試樣本在6個二分類器上面進行預測。從結果上可以看到,類別1被預測的最多,故測試樣本屬於類別1。
 

2.2 一對其余 (OvR)

一對其余其實更加好理解,每次將一個類別作為正類,其余類別作為負類。此時共有(N個分類器)。在測試的時候若僅有一個分類器預測為正類,則對應的類別標記為最終的分類結果。例如下面這個例子。
  大概解釋一下,就是有當有4個類別的時候,每次把其中一個類別作為正類別,其余作為負類別,共有4種組合,對於這4中組合進行分類器的訓練,我們可以得到4個分類器。對於測試樣本,放進4個分類器進行預測,僅有一個分類器預測為正類,於是取這個分類器的結果作為預測結果,分類器2預測的結果是類別2,於是這個樣本便屬於類別2。
  其實,有人會有疑問,那么預測為負類的分類器就不用管了嗎?是的,因為預測為負類的時候有多種可能,無法確定,只有預測為正類的時候才能唯一確定屬於哪一類。比如對於分類器3,分類結果是負類,但是負類有類別1,類別2,類別4三種,到底屬於哪一種?

2.3 多對多(MvM)

  所謂多對多其實就是把多個類別作為正類,多個類別作為負類。本文不介紹這個方法,詳細可以參考周志華西瓜書P64-P65。
 

3,對於上面的方法其實都是訓練多個二分類器,那么有沒有更加直接的方法對LR來進行多分類呢?

我們知道,對於二分類的LR時,正類和負類的概率分別如下:
 
 
對於多分類,其實我只需要做簡單的修改就可以了。
假設某分類任務有K個類別,那么對於每一個類別的概率有:
對於第K類來說
 
 
對於其余類而言
 

一,問題描述

  在本文學習中,我們將使用鳶尾花數據集的標准機器學習問題。

  這個數據集經過深入研究,是在神經網絡上練習的一個很好的問題,因為所有4個輸入變量都是數字的,並且具有相同的厘米級別。每個實例描述觀察到的花測量的屬性,輸出變量是特定的鳶尾種類。

  這是一個多類別的分類問題,意味着有兩個以上的類需要預測,實際上有三種花種。這是用神經網絡練習的一個重要問題類型,因為三個類值需要專門的處理。

  鳶尾花數據集是一個充分研究的問題,我們可以期望實現模型精度為在95%至97%的范圍內,這為開發我們的模型提供了一個很好的目標。

  您可以從UCI機器學習庫下載鳶尾花數據集,並將其放在當前工作目錄中,文件名為 “ iris.csv”。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
4.6,3.4,1.4,0.3,Iris-setosa
5.0,3.4,1.5,0.2,Iris-setosa
4.4,2.9,1.4,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
5.4,3.7,1.5,0.2,Iris-setosa
4.8,3.4,1.6,0.2,Iris-setosa
4.8,3.0,1.4,0.1,Iris-setosa
4.3,3.0,1.1,0.1,Iris-setosa
5.8,4.0,1.2,0.2,Iris-setosa
5.7,4.4,1.5,0.4,Iris-setosa
5.4,3.9,1.3,0.4,Iris-setosa
5.1,3.5,1.4,0.3,Iris-setosa
5.7,3.8,1.7,0.3,Iris-setosa
5.1,3.8,1.5,0.3,Iris-setosa
5.4,3.4,1.7,0.2,Iris-setosa
5.1,3.7,1.5,0.4,Iris-setosa
4.6,3.6,1.0,0.2,Iris-setosa
5.1,3.3,1.7,0.5,Iris-setosa
4.8,3.4,1.9,0.2,Iris-setosa
5.0,3.0,1.6,0.2,Iris-setosa
5.0,3.4,1.6,0.4,Iris-setosa
5.2,3.5,1.5,0.2,Iris-setosa
5.2,3.4,1.4,0.2,Iris-setosa
4.7,3.2,1.6,0.2,Iris-setosa
4.8,3.1,1.6,0.2,Iris-setosa
5.4,3.4,1.5,0.4,Iris-setosa
5.2,4.1,1.5,0.1,Iris-setosa
5.5,4.2,1.4,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
5.0,3.2,1.2,0.2,Iris-setosa
5.5,3.5,1.3,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
4.4,3.0,1.3,0.2,Iris-setosa
5.1,3.4,1.5,0.2,Iris-setosa
5.0,3.5,1.3,0.3,Iris-setosa
4.5,2.3,1.3,0.3,Iris-setosa
4.4,3.2,1.3,0.2,Iris-setosa
5.0,3.5,1.6,0.6,Iris-setosa
5.1,3.8,1.9,0.4,Iris-setosa
4.8,3.0,1.4,0.3,Iris-setosa
5.1,3.8,1.6,0.2,Iris-setosa
4.6,3.2,1.4,0.2,Iris-setosa
5.3,3.7,1.5,0.2,Iris-setosa
5.0,3.3,1.4,0.2,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
6.5,2.8,4.6,1.5,Iris-versicolor
5.7,2.8,4.5,1.3,Iris-versicolor
6.3,3.3,4.7,1.6,Iris-versicolor
4.9,2.4,3.3,1.0,Iris-versicolor
6.6,2.9,4.6,1.3,Iris-versicolor
5.2,2.7,3.9,1.4,Iris-versicolor
5.0,2.0,3.5,1.0,Iris-versicolor
5.9,3.0,4.2,1.5,Iris-versicolor
6.0,2.2,4.0,1.0,Iris-versicolor
6.1,2.9,4.7,1.4,Iris-versicolor
5.6,2.9,3.6,1.3,Iris-versicolor
6.7,3.1,4.4,1.4,Iris-versicolor
5.6,3.0,4.5,1.5,Iris-versicolor
5.8,2.7,4.1,1.0,Iris-versicolor
6.2,2.2,4.5,1.5,Iris-versicolor
5.6,2.5,3.9,1.1,Iris-versicolor
5.9,3.2,4.8,1.8,Iris-versicolor
6.1,2.8,4.0,1.3,Iris-versicolor
6.3,2.5,4.9,1.5,Iris-versicolor
6.1,2.8,4.7,1.2,Iris-versicolor
6.4,2.9,4.3,1.3,Iris-versicolor
6.6,3.0,4.4,1.4,Iris-versicolor
6.8,2.8,4.8,1.4,Iris-versicolor
6.7,3.0,5.0,1.7,Iris-versicolor
6.0,2.9,4.5,1.5,Iris-versicolor
5.7,2.6,3.5,1.0,Iris-versicolor
5.5,2.4,3.8,1.1,Iris-versicolor
5.5,2.4,3.7,1.0,Iris-versicolor
5.8,2.7,3.9,1.2,Iris-versicolor
6.0,2.7,5.1,1.6,Iris-versicolor
5.4,3.0,4.5,1.5,Iris-versicolor
6.0,3.4,4.5,1.6,Iris-versicolor
6.7,3.1,4.7,1.5,Iris-versicolor
6.3,2.3,4.4,1.3,Iris-versicolor
5.6,3.0,4.1,1.3,Iris-versicolor
5.5,2.5,4.0,1.3,Iris-versicolor
5.5,2.6,4.4,1.2,Iris-versicolor
6.1,3.0,4.6,1.4,Iris-versicolor
5.8,2.6,4.0,1.2,Iris-versicolor
5.0,2.3,3.3,1.0,Iris-versicolor
5.6,2.7,4.2,1.3,Iris-versicolor
5.7,3.0,4.2,1.2,Iris-versicolor
5.7,2.9,4.2,1.3,Iris-versicolor
6.2,2.9,4.3,1.3,Iris-versicolor
5.1,2.5,3.0,1.1,Iris-versicolor
5.7,2.8,4.1,1.3,Iris-versicolor
6.3,3.3,6.0,2.5,Iris-virginica
5.8,2.7,5.1,1.9,Iris-virginica
7.1,3.0,5.9,2.1,Iris-virginica
6.3,2.9,5.6,1.8,Iris-virginica
6.5,3.0,5.8,2.2,Iris-virginica
7.6,3.0,6.6,2.1,Iris-virginica
4.9,2.5,4.5,1.7,Iris-virginica
7.3,2.9,6.3,1.8,Iris-virginica
6.7,2.5,5.8,1.8,Iris-virginica
7.2,3.6,6.1,2.5,Iris-virginica
6.5,3.2,5.1,2.0,Iris-virginica
6.4,2.7,5.3,1.9,Iris-virginica
6.8,3.0,5.5,2.1,Iris-virginica
5.7,2.5,5.0,2.0,Iris-virginica
5.8,2.8,5.1,2.4,Iris-virginica
6.4,3.2,5.3,2.3,Iris-virginica
6.5,3.0,5.5,1.8,Iris-virginica
7.7,3.8,6.7,2.2,Iris-virginica
7.7,2.6,6.9,2.3,Iris-virginica
6.0,2.2,5.0,1.5,Iris-virginica
6.9,3.2,5.7,2.3,Iris-virginica
5.6,2.8,4.9,2.0,Iris-virginica
7.7,2.8,6.7,2.0,Iris-virginica
6.3,2.7,4.9,1.8,Iris-virginica
6.7,3.3,5.7,2.1,Iris-virginica
7.2,3.2,6.0,1.8,Iris-virginica
6.2,2.8,4.8,1.8,Iris-virginica
6.1,3.0,4.9,1.8,Iris-virginica
6.4,2.8,5.6,2.1,Iris-virginica
7.2,3.0,5.8,1.6,Iris-virginica
7.4,2.8,6.1,1.9,Iris-virginica
7.9,3.8,6.4,2.0,Iris-virginica
6.4,2.8,5.6,2.2,Iris-virginica
6.3,2.8,5.1,1.5,Iris-virginica
6.1,2.6,5.6,1.4,Iris-virginica
7.7,3.0,6.1,2.3,Iris-virginica
6.3,3.4,5.6,2.4,Iris-virginica
6.4,3.1,5.5,1.8,Iris-virginica
6.0,3.0,4.8,1.8,Iris-virginica
6.9,3.1,5.4,2.1,Iris-virginica
6.7,3.1,5.6,2.4,Iris-virginica
6.9,3.1,5.1,2.3,Iris-virginica
5.8,2.7,5.1,1.9,Iris-virginica
6.8,3.2,5.9,2.3,Iris-virginica
6.7,3.3,5.7,2.5,Iris-virginica
6.7,3.0,5.2,2.3,Iris-virginica
6.3,2.5,5.0,1.9,Iris-virginica
6.5,3.0,5.2,2.0,Iris-virginica
6.2,3.4,5.4,2.3,Iris-virginica
5.9,3.0,5.1,1.8,Iris-virginica

 

二,導入類和函數

  我們從導入本文需要的所有類和函數開始。其中包括需要Keras的功能,還包括來自pandas的數據加載以及來自scikit-learn的數據准備和模型評估。

1
2
3
4
5
6
7
8
9
10
import numpy
import pandas
from  keras.models import Sequential
from  keras.layers import Dense
from  keras.wrappers.scikit_learn import KerasClassifier
from  keras.utils import np_utils
from  sklearn.model_selection import cross_val_score
from  sklearn.model_selection import KFold
from  sklearn.preprocessing import LabelEncoder
from  sklearn.pipeline import Pipeline

 

三,初始化隨機數生成器

  下面,我們將隨機數生成器初始化為常量值(7)

  這對於確保我們可以再次精確地實現從該模型獲得的結果非常重要,它確保可以再現訓練神經網絡模型的隨機過程。

1
2
3
# fix random seed for reproducibility
seed = 7
numpy.random.seed(seed)

 

四,記載數據集

  可以直接加載數據集。因為輸出變量包含字符串,所以最容易使用pandas加載數據。然后我們可以將屬性(列)拆分為輸入變量(X)和輸出變量(Y)。

1
2
3
4
5
# load dataset
dataframe = pandas.read_csv( "iris.csv" , header=None)
dataset = dataframe.values
X = dataset[:,0:4].astype( float )
Y = dataset[:,4]

 

五,編碼輸出變量

  輸出變量包含三個不同的字符串值。

  當使用神經網絡對多類分類問題進行建模時,優良作法是將包含每個類值的值的向量的輸出屬性重新整形為一個矩陣,每個類值都有一個布爾值,以及給定實例是否具有該值是否有類值。

  這稱為one hot encoding  或者從分類變量創建虛擬變量。

  例如:在這個問題中,三個類值是Iris-setosa,Iris-versicolor和Iris-virginica。如果我們有觀察結果:

  多類分類問題本質上可以分解為多個二分類問題,而解決二分類問題的方法有很多。這里我們利用Keras機器學習框架中的ANN(artificial neural network)來解決多分類問題。這里我們采用的例子是著名的UCI Machine Learning Repository中的鳶尾花數據集(iris flower dataset)。
  多類分類問題與二類分類問題類似,需要將類別變量(categorical function)的輸出標簽轉化為數值變量。這個問題在二分類的時候直接轉換為(0,1)(輸出層采用sigmoid函數)或(-1,1)(輸出層采用tanh函數)。類似的,在多分類問題中我們將轉化為虛擬變量(dummy variable):即用one hot encoding方法將輸出標簽的向量(vector)轉化為只在出現對應標簽的那一列為1,其余為0的布爾矩陣。以我們所用的鳶尾花數據為例:

1
2
3
4
sample,    label
1,        Iris-setosa
2,        Iris-versicolor
3,        Iris-virginica

 用one hot encoding轉化后如下:

1
2
3
4
sample, Iris-setosa, Iris-versicolor, Iris-virginica
1,        1,            0,                0
2,        0,            1,                0
3,        0,            0,                1

   注意這里不要將label直接轉化成數值變量,如1,2,3,這樣的話與其說是預測問題更像是回歸預測的問題,后者的難度比前者大。(當類別比較多的時候輸出值的跨度就會比較大,此時輸出層的激活函數就只能用linear)

 

我們可以通過首先使用scikit-learn類LabelEncoder將字符串一致地編碼為整數來完成此操作。然后使用Keras函數to_categorical()將整數向量轉換為一個熱編碼

1
2
3
4
5
6
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
# convert integers to dummy variables (i.e. one hot encoded)
dummy_y = np_utils.to_categorical(encoded_Y)

 六,定義神經網絡模型

  Keras庫提供了包裝類,允許您在scikit-learn中使用Keras開發的神經網絡模型。

  Keras中有一個KerasClassifier類,可用作scikit-learn中的Estimator,它是庫中基本類型的模型。KerasClassifier將函數的名稱作為參數。該函數必須返回構建的神經網絡模型,為訓練做好准備。

  下面是一個函數,它將為鳶尾花分類問題創建一個基線神經網絡。它創建了一個簡單的完全連接的網絡,其中一個隱藏層包含8個神經元。

  隱藏層使用整流器激活功能,這是一種很好的做法。因為我們對鳶尾花數據集使用了單熱編碼,所以輸出層必須創建3個輸出值,每個類一個。具有最大值的輸出值將被視為模型預測的類。

  這個簡單的單層神經網絡的網絡拓撲可以概括為:

1
4 inputs -> [8 hidden nodes] -> 3 outputs

  請注意,我們在輸出層使用“ softmax ”激活功能。這是為了確保輸出值在0和1的范圍內,並且可以用作預測概率。

  最后,網絡使用具有對數損失函數的高效Adam梯度下降優化算法,在Keras中稱為“ categorical_crossentropy ”。

1
2
3
4
5
6
7
8
9
# define baseline model
def baseline_model():
     # create model
     model = Sequential()
     model.add(Dense(8, input_dim=4, activation= 'relu' ))
     model.add(Dense(3, activation= 'softmax' ))
     # Compile model
     model.compile(loss= 'categorical_crossentropy' , optimizer= 'adam' , metrics=[ 'accuracy' ])
     return  model

   我們現在可以創建我們的KerasClassifier用於scikit-learn。

  我們還可以在構造KerasClassifier類中傳遞參數,該類將傳遞給內部用於訓練神經網絡的fit()函數。在這里,我們將時期數量傳遞為200,批量大小為5,以便在訓練模型時使用。通過將verbose設置為0,在訓練時也會關閉調試。

1
estimator = KerasClassifier(build_fn=baseline_model, epochs=200, batch_size=5, verbose=0)

 七,使用k-fold交叉驗證評估模型

Keras是基於Theano或Tensorflow底層開發的簡單模塊化的神經網絡框架,因此用Keras搭建網絡結構會比Tensorflow更加簡單。這里我們將使用Keras提供的KerasClassifier類,這個類可以在scikit-learn包中作為Estimator使用,故利用這個類我們就可以方便的調用sklearn包中的一些函數進行數據預處理和結果評估(此為sklearn包中模型(model)的基本類型)。
  對於網絡結構,我們采用3層全向連接的,輸入層有4個節點,隱含層有10個節點,輸出層有3個節點的網絡。其中,隱含層的激活函數為relu(rectifier),輸出層的激活函數為softmax。損失函數則相應的選擇categorical_crossentropy(此函數來着theano或tensorflow,具體可以參見這里)(二分類的話一般選擇activation=‘sigmoid’, loss=‘binary_crossentropy’)。
  PS:對於多類分類網絡結構而言,增加中間隱含層能夠提升訓練精度,但是所需的計算時間和空間會增大,因此需要測試選擇一個合適的數目,這里我們設為10;此外,每一層的舍棄率(dropout)也需要相應調整(太高容易欠擬合,太低容易過擬合),這里我們設為0.2。

  我們現在可以在訓練數據上評估神經網絡模型。

  scikit-learn具有使用一套技術評估模型的出色能力。評估機器學習模型的黃金標准是k倍交叉驗證。

  首先,我們可以定義模型評估程序。在這里,我們將折疊數設置為10(一個很好的默認值)並在分區之前對數據進行洗牌。

1
kfold = KFold(n_splits=10, shuffle=True, random_state=seed)

 

  現在我們可以使用10倍交叉驗證程序(kfold)在我們的數據集(X和dummy_y)上評估我們的模型(估計器)。

  評估模型僅需要大約10秒鍾,並返回一個對象,該對象描述了對數據集的每個分割的10個構建模型的評估。

1
2
results = cross_val_score(estimator, X, dummy_y, cv=kfold)
print( "Baseline: %.2f%% (%.2f%%)"  % (results.mean()*100, results.std()*100))

   結果總結為數據集上模型精度的均值和標准差。這是對看不見的數據的模型性能的合理估計。對於這個問題,它也屬於已知的最佳結果范圍。

1
Accuracy: 97.33% (4.42%)

八, 代碼實現

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
37
38
39
40
41
42
43
44
45
46
import numpy  as  np
import pandas  as  pd
from  keras.models import Sequential
from  keras.layers import Dense, Dropout
from  keras.wrappers.scikit_learn import KerasClassifier
from  keras.utils import np_utils
from  sklearn.model_selection import train_test_split, KFold, cross_val_score
from  sklearn.preprocessing import LabelEncoder
 
# load dataset
dataframe = pd.read_csv( "iris.csv" , header=None)
dataset = dataframe.values
X = dataset[:, 0:4].astype( float )
Y = dataset[:, 4]
 
# encode class values as integers
encoder = LabelEncoder()
encoded_Y = encoder.fit_transform(Y)
# convert integers to dummy variables (one hot encoding)
dummy_y = np_utils.to_categorical(encoded_Y)
 
# define model structure
def baseline_model():
     model = Sequential()
     model.add(Dense(output_dim=10, input_dim=4, activation= 'relu' ))
     model.add(Dropout(0.2))
     model.add(Dense(output_dim=3, input_dim=10, activation= 'softmax' ))
     # Compile model
     model.compile(loss= 'categorical_crossentropy' , optimizer= 'adam' , metrics=[ 'accuracy' ])
     return  model
estimator = KerasClassifier(build_fn=baseline_model, nb_epoch=40, batch_size=256)
# splitting data into training set and test set. If random_state is set to an integer, the split datasets are fixed.
X_train, X_test, Y_train, Y_test = train_test_split(X, dummy_y, test_size=0.3, random_state=0)
estimator.fit(X_train, Y_train)
 
# make predictions
pred = estimator.predict(X_test)
 
# inverse numeric variables to initial categorical labels
init_lables = encoder.inverse_transform(pred)
 
# k-fold cross-validate
seed = 42
np.random.seed(seed)
kfold = KFold(n_splits=10, shuffle=True, random_state=seed)
results = cross_val_score(estimator, X, dummy_y, cv=kfold)

 九,總結

在這篇文章中,我們學習了如何使用Keras Python庫開發和評估神經網絡以進行深度學習。學習了以下知識:

  • 如何加載數據並使其可用於Keras。
  • 如何使用一個熱編碼准備多類分類數據進行建模。
  • 如何使用keras神經網絡模型與scikit-learn。
  • 如何使用Keras定義神經網絡進行多類分類。
  • 如何使用帶有k-fold交叉驗證的scikit-learn來評估Keras神經網絡模型

十,參考

  1. http://machinelearningmastery.com/multi-class-classification-tutorial-keras-deep-learning-library/
  2. http://datascience.stackexchange.com/questions/10048/what-is-the-best-keras-model-for-multi-label-classification
  3. http://stackoverflow.com/questions/28064634/random-state-pseudo-random-numberin-scikit-learn
  4. http://scikit-learn.org/stable/modules/classes.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM