Predicción de ciberataques analizando el trafico de red
Machine Learning predictivo
Predicción de ciberataques analizando el trafico de red
- 1. Carga de datos
- 2. Análisis exploratorio de los datos
- 2.1. Analisis exploratorio inicial y tipo de datos
- 2.2. Estadísticos básicos
- 2.3. Calidad de datos: análisis de nulos
- 2.4. Calidad de datos: análisis de ceros
- 2.5. Calidad de datos: análisis de atípicos
- 2.6 - Analisis longitudinal
- 2.7 - Analisis de coherencia
- 2.8. Analisis de la variable target
- 2.9 - Acciones resultado del analisis de calidad de datos y exploratorio
- 3 - Trasformación de datos
- 4 - Creacion de variables sinteticas
- 5. Modelizacion
1. Carga de datos
Cargamos los datos de red que tenemos disponibles. Este dataset es publico y ha sido descargado de BigML.
df <- fread('intrusion_data.csv')
print(head(df))## duration protocol_type service flag src_bytes dst_bytes land
## 1: 0 tcp http SF 181 5450 0
## 2: 0 tcp http SF 239 486 0
## 3: 0 tcp http SF 235 1337 0
## 4: 0 tcp http SF 219 1337 0
## 5: 0 tcp http SF 217 2032 0
## 6: 0 tcp http SF 217 2032 0
## wrong_fragment urgent hot num_failed_logins logged_in num_compromised
## 1: 0 0 0 0 1 0
## 2: 0 0 0 0 1 0
## 3: 0 0 0 0 1 0
## 4: 0 0 0 0 1 0
## 5: 0 0 0 0 1 0
## 6: 0 0 0 0 1 0
## root_shell su_attempted num_root num_file_creations num_shells
## 1: 0 0 0 0 0
## 2: 0 0 0 0 0
## 3: 0 0 0 0 0
## 4: 0 0 0 0 0
## 5: 0 0 0 0 0
## 6: 0 0 0 0 0
## num_access_files num_outbound_cmds is_host_login is_guest_login count
## 1: 0 0 0 0 8
## 2: 0 0 0 0 8
## 3: 0 0 0 0 8
## 4: 0 0 0 0 6
## 5: 0 0 0 0 6
## 6: 0 0 0 0 6
## srv_count serror_rate srv_serror_rate rerror_rate srv_rerror_rate
## 1: 8 0 0 0 0
## 2: 8 0 0 0 0
## 3: 8 0 0 0 0
## 4: 6 0 0 0 0
## 5: 6 0 0 0 0
## 6: 6 0 0 0 0
## same_srv_rate diff_srv_rate srv_diff_host_rate dst_host_count
## 1: 1 0 0 9
## 2: 1 0 0 19
## 3: 1 0 0 29
## 4: 1 0 0 39
## 5: 1 0 0 49
## 6: 1 0 0 59
## dst_host_srv_count dst_host_same_srv_rate dst_host_diff_srv_rate
## 1: 9 1 0
## 2: 19 1 0
## 3: 29 1 0
## 4: 39 1 0
## 5: 49 1 0
## 6: 59 1 0
## dst_host_same_src_port_rate dst_host_srv_diff_host_rate
## 1: 0.11 0
## 2: 0.05 0
## 3: 0.03 0
## 4: 0.03 0
## 5: 0.02 0
## 6: 0.02 0
## dst_host_serror_rate dst_host_srv_serror_rate dst_host_rerror_rate
## 1: 0 0 0
## 2: 0 0 0
## 3: 0 0 0
## 4: 0 0 0
## 5: 0 0 0
## 6: 0 0 0
## dst_host_srv_rerror_rate connection
## 1: 0 normal
## 2: 0 normal
## 3: 0 normal
## 4: 0 normal
## 5: 0 normal
## 6: 0 normal
El dataset consta de 494.021 observaciones y de 42 variables. El objetivo es predecir si la conexión es un intento de intrusión o ciberataque a la red. La variable a predecir es connection.
El tamaño del dataset es manejable, asi que no haremos muestreo.
2. Análisis exploratorio de los datos
2.1. Analisis exploratorio inicial y tipo de datos
Vamos a ver que tipo de datos de cada una de las variables.
as.data.frame(sort(names(df)))## sort(names(df))
## 1 connection
## 2 count
## 3 diff_srv_rate
## 4 dst_bytes
## 5 dst_host_count
## 6 dst_host_diff_srv_rate
## 7 dst_host_rerror_rate
## 8 dst_host_same_src_port_rate
## 9 dst_host_same_srv_rate
## 10 dst_host_serror_rate
## 11 dst_host_srv_count
## 12 dst_host_srv_diff_host_rate
## 13 dst_host_srv_rerror_rate
## 14 dst_host_srv_serror_rate
## 15 duration
## 16 flag
## 17 hot
## 18 is_guest_login
## 19 is_host_login
## 20 land
## 21 logged_in
## 22 num_access_files
## 23 num_compromised
## 24 num_failed_logins
## 25 num_file_creations
## 26 num_outbound_cmds
## 27 num_root
## 28 num_shells
## 29 protocol_type
## 30 rerror_rate
## 31 root_shell
## 32 same_srv_rate
## 33 serror_rate
## 34 service
## 35 src_bytes
## 36 srv_count
## 37 srv_diff_host_rate
## 38 srv_rerror_rate
## 39 srv_serror_rate
## 40 su_attempted
## 41 urgent
## 42 wrong_fragment
str(df)## Classes 'data.table' and 'data.frame': 494021 obs. of 42 variables:
## $ duration : int 0 0 0 0 0 0 0 0 0 0 ...
## $ protocol_type : chr "tcp" "tcp" "tcp" "tcp" ...
## $ service : chr "http" "http" "http" "http" ...
## $ flag : chr "SF" "SF" "SF" "SF" ...
## $ src_bytes : int 181 239 235 219 217 217 212 159 210 212 ...
## $ dst_bytes : int 5450 486 1337 1337 2032 2032 1940 4087 151 786 ...
## $ land : int 0 0 0 0 0 0 0 0 0 0 ...
## $ wrong_fragment : int 0 0 0 0 0 0 0 0 0 0 ...
## $ urgent : int 0 0 0 0 0 0 0 0 0 0 ...
## $ hot : int 0 0 0 0 0 0 0 0 0 1 ...
## $ num_failed_logins : int 0 0 0 0 0 0 0 0 0 0 ...
## $ logged_in : int 1 1 1 1 1 1 1 1 1 1 ...
## $ num_compromised : int 0 0 0 0 0 0 0 0 0 0 ...
## $ root_shell : int 0 0 0 0 0 0 0 0 0 0 ...
## $ su_attempted : int 0 0 0 0 0 0 0 0 0 0 ...
## $ num_root : int 0 0 0 0 0 0 0 0 0 0 ...
## $ num_file_creations : int 0 0 0 0 0 0 0 0 0 0 ...
## $ num_shells : int 0 0 0 0 0 0 0 0 0 0 ...
## $ num_access_files : int 0 0 0 0 0 0 0 0 0 0 ...
## $ num_outbound_cmds : int 0 0 0 0 0 0 0 0 0 0 ...
## $ is_host_login : int 0 0 0 0 0 0 0 0 0 0 ...
## $ is_guest_login : int 0 0 0 0 0 0 0 0 0 0 ...
## $ count : int 8 8 8 6 6 6 1 5 8 8 ...
## $ srv_count : int 8 8 8 6 6 6 2 5 8 8 ...
## $ serror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ srv_serror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ rerror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ srv_rerror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ same_srv_rate : num 1 1 1 1 1 1 1 1 1 1 ...
## $ diff_srv_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ srv_diff_host_rate : num 0 0 0 0 0 0 1 0 0 0 ...
## $ dst_host_count : int 9 19 29 39 49 59 1 11 8 8 ...
## $ dst_host_srv_count : int 9 19 29 39 49 59 69 79 89 99 ...
## $ dst_host_same_srv_rate : num 1 1 1 1 1 1 1 1 1 1 ...
## $ dst_host_diff_srv_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ dst_host_same_src_port_rate: num 0.11 0.05 0.03 0.03 0.02 0.02 1 0.09 0.12 0.12 ...
## $ dst_host_srv_diff_host_rate: num 0 0 0 0 0 0 0.04 0.04 0.04 0.05 ...
## $ dst_host_serror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ dst_host_srv_serror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ dst_host_rerror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ dst_host_srv_rerror_rate : num 0 0 0 0 0 0 0 0 0 0 ...
## $ connection : chr "normal" "normal" "normal" "normal" ...
## - attr(*, ".internal.selfref")=<externalptr>
glimpse(df)## Observations: 494,021
## Variables: 42
## $ duration <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ protocol_type <chr> "tcp", "tcp", "tcp", "tcp", "tcp",...
## $ service <chr> "http", "http", "http", "http", "h...
## $ flag <chr> "SF", "SF", "SF", "SF", "SF", "SF"...
## $ src_bytes <int> 181, 239, 235, 219, 217, 217, 212,...
## $ dst_bytes <int> 5450, 486, 1337, 1337, 2032, 2032,...
## $ land <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ wrong_fragment <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ urgent <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ hot <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0...
## $ num_failed_logins <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ logged_in <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ num_compromised <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ root_shell <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ su_attempted <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ num_root <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ num_file_creations <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ num_shells <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ num_access_files <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ num_outbound_cmds <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ is_host_login <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ is_guest_login <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ count <int> 8, 8, 8, 6, 6, 6, 1, 5, 8, 8, 18, ...
## $ srv_count <int> 8, 8, 8, 6, 6, 6, 2, 5, 8, 8, 18, ...
## $ serror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ srv_serror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ rerror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ srv_rerror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ same_srv_rate <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ diff_srv_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ srv_diff_host_rate <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00...
## $ dst_host_count <int> 9, 19, 29, 39, 49, 59, 1, 11, 8, 8...
## $ dst_host_srv_count <int> 9, 19, 29, 39, 49, 59, 69, 79, 89,...
## $ dst_host_same_srv_rate <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
## $ dst_host_diff_srv_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ dst_host_same_src_port_rate <dbl> 0.11, 0.05, 0.03, 0.03, 0.02, 0.02...
## $ dst_host_srv_diff_host_rate <dbl> 0.00, 0.00, 0.00, 0.00, 0.00, 0.00...
## $ dst_host_serror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ dst_host_srv_serror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ dst_host_rerror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ dst_host_srv_rerror_rate <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
## $ connection <chr> "normal", "normal", "normal", "nor...
Tenemos los siguientes tipos de variables:
- Variables tipo integer o num: duration, src_bytes, dst_bytes, count, srv_count, dst_host_count, dst_host_srv_count, dst_host_same_src_port_rate, dst_host_srv_diff_host_rate.
- Variables tipo character: protocol_type, service, flag, connection.
- Variables integer o num pero que son factores: land, wrong_fragment, urgent, hot, num_failed_logins, logged_in, num_compromised, root_shell, su_attempted, num_root, num_file_creations, num_shells, num_access_files, num_outbound_cmds, is_host_login, is_guest_login, serror_rate, srv_serror_rate, rerror_rate, srv_rerror_rate, same_srv_rate, diff_srv_rate, srv_diff_host_rate, dst_host_same_srv_rate, dst_host_diff_srv_rate, dst_host_serror_rate, dst_host_srv_serror_rate, dst_host_rerror_rate, dst_host_srv_rerror_rate.
2.2. Estadísticos básicos
Hacemos una analisis estadistico básico de las variables, con el que podremos confirmar que variables son factores, que variables tienen atípicos, valores nulos, valores cero.
est_basicos <- lapply(df,summary)
print(est_basicos)## $duration
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 0.00 0.00 47.98 0.00 58329.00
##
## $protocol_type
## Length Class Mode
## 494021 character character
##
## $service
## Length Class Mode
## 494021 character character
##
## $flag
## Length Class Mode
## 494021 character character
##
## $src_bytes
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 45 520 3026 1032 693375640
##
## $dst_bytes
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 0 0 869 0 5155468
##
## $land
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000000 0.0000000 0.0000000 0.0000445 0.0000000 1.0000000
##
## $wrong_fragment
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.006433 0.000000 3.000000
##
## $urgent
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000000 0.0000000 0.0000000 0.0000142 0.0000000 3.0000000
##
## $hot
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.03452 0.00000 30.00000
##
## $num_failed_logins
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.000152 0.000000 5.000000
##
## $logged_in
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.1482 0.0000 1.0000
##
## $num_compromised
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.0102 0.0000 884.0000
##
## $root_shell
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000000 0.0000000 0.0000000 0.0001113 0.0000000 1.0000000
##
## $su_attempted
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000000 0.0000000 0.0000000 0.0000364 0.0000000 2.0000000
##
## $num_root
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.0114 0.0000 993.0000
##
## $num_file_creations
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.001083 0.000000 28.000000
##
## $num_shells
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000000 0.0000000 0.0000000 0.0001093 0.0000000 2.0000000
##
## $num_access_files
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.001008 0.000000 8.000000
##
## $num_outbound_cmds
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 0 0 0 0 0
##
## $is_host_login
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0 0 0 0 0 0
##
## $is_guest_login
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.001387 0.000000 1.000000
##
## $count
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0 117.0 510.0 332.3 511.0 511.0
##
## $srv_count
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0 10.0 510.0 292.9 511.0 511.0
##
## $serror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.1767 0.0000 1.0000
##
## $srv_serror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.1766 0.0000 1.0000
##
## $rerror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.05743 0.00000 1.00000
##
## $srv_rerror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.05772 0.00000 1.00000
##
## $same_srv_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 1.0000 1.0000 0.7915 1.0000 1.0000
##
## $diff_srv_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.02098 0.00000 1.00000
##
## $srv_diff_host_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 0.000 0.000 0.029 0.000 1.000
##
## $dst_host_count
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0 255.0 255.0 232.5 255.0 255.0
##
## $dst_host_srv_count
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0 46.0 255.0 188.7 255.0 255.0
##
## $dst_host_same_srv_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.4100 1.0000 0.7538 1.0000 1.0000
##
## $dst_host_diff_srv_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.03091 0.04000 1.00000
##
## $dst_host_same_src_port_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 1.0000 0.6019 1.0000 1.0000
##
## $dst_host_srv_diff_host_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000000 0.000000 0.000000 0.006684 0.000000 1.000000
##
## $dst_host_serror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.1768 0.0000 1.0000
##
## $dst_host_srv_serror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.0000 0.1764 0.0000 1.0000
##
## $dst_host_rerror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.05812 0.00000 1.00000
##
## $dst_host_srv_rerror_rate
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00000 0.00000 0.00000 0.05741 0.00000 1.00000
##
## $connection
## Length Class Mode
## 494021 character character
Conclusiones:
- Variables tipo num o integer: duration, src_bytes, dst_bytes, count, srv_count, dst_host_count, dst_host_srv_count, wrong_fragment, urgent, hot, num_failed_logins, num_compromised, num_root, num_file_creations.
- Variables tipo character: protocol_type, service, flag, connection. Estas variables las pasaremos a factores directamente.
- Variables integer o num pero cambiaremos a factores directamente: land, logged_in, root_shell, su_attempted, num_shells, num_access_files, num_outbound_cmds, is_host_login, is_guest_login.
- Las variables que tienen sufijo _rate(serror_rate, srv_serror_rate, rerror_rate, srv_rerror_rate, same_srv_rate, diff_srv_rate, srv_diff_host_rate, dst_host_same_srv_rate, dst_host_diff_srv_rate, dst_host_same_src_port_rate, dst_host_srv_diff_host_rate, dst_host_serror_rate, dst_host_srv_serror_rate, dst_host_rerror_rate, dst_host_srv_rerror_rate) son ratios que van de cero a uno. Estas variables las discretizaremos, ya que estos ratios tienen muchos atipicos, sus estadísticos están muy sesgados. Con la discretización evitamos estos problemas.
2.3. Calidad de datos: análisis de nulos
Vamos a comprobar si tenemos nulos en nuestro dataset.
num_nulos <- data.frame(colSums(is.na(df)))
print(num_nulos)## colSums.is.na.df..
## duration 0
## protocol_type 0
## service 0
## flag 0
## src_bytes 0
## dst_bytes 0
## land 0
## wrong_fragment 0
## urgent 0
## hot 0
## num_failed_logins 0
## logged_in 0
## num_compromised 0
## root_shell 0
## su_attempted 0
## num_root 0
## num_file_creations 0
## num_shells 0
## num_access_files 0
## num_outbound_cmds 0
## is_host_login 0
## is_guest_login 0
## count 0
## srv_count 0
## serror_rate 0
## srv_serror_rate 0
## rerror_rate 0
## srv_rerror_rate 0
## same_srv_rate 0
## diff_srv_rate 0
## srv_diff_host_rate 0
## dst_host_count 0
## dst_host_srv_count 0
## dst_host_same_srv_rate 0
## dst_host_diff_srv_rate 0
## dst_host_same_src_port_rate 0
## dst_host_srv_diff_host_rate 0
## dst_host_serror_rate 0
## dst_host_srv_serror_rate 0
## dst_host_rerror_rate 0
## dst_host_srv_rerror_rate 0
## connection 0
No tenemos datos nulos en el dataset.
2.4. Calidad de datos: análisis de ceros
Vamos a ver que variables tienen ceros, y si son normales o datos a tratar.
contar_ceros <- function(variable) {
temp <- transmute(df,if_else(variable==0,1,0))
sum(temp)
}
num_ceros <- sapply(df,contar_ceros) #aplico la función de contar ceros a cada variable de nuestro conjunto de datos
#transformamos el vector en un data frame para mejorar su visualización
num_ceros <- data.frame(VARIABLE=names(num_ceros),CEROS = as.numeric(num_ceros),stringsAsFactors = F) #el as.numeric es para sacar solo el valor de num_ceros, sin el nombre
num_ceros <- num_ceros %>%
arrange(desc(CEROS)) %>%
mutate(PORCENTAJE = CEROS / nrow(df) * 100) #añado a los valores absolutos el porcentajes del total de observaciones
num_ceros## VARIABLE CEROS PORCENTAJE
## 1 num_outbound_cmds 494021 100.0000000000
## 2 is_host_login 494021 100.0000000000
## 3 urgent 494017 99.9991903178
## 4 su_attempted 494009 99.9975709535
## 5 land 493999 99.9955467480
## 6 num_shells 493970 99.9896765522
## 7 root_shell 493966 99.9888668700
## 8 num_failed_logins 493958 99.9872475057
## 9 num_file_creations 493756 99.9463585556
## 10 num_access_files 493567 99.9081010726
## 11 num_root 493436 99.8815839812
## 12 is_guest_login 493336 99.8613419268
## 13 wrong_fragment 492783 99.7494033654
## 14 num_compromised 491797 99.5498167082
## 15 hot 490829 99.3538736208
## 16 duration 481671 97.5001062708
## 17 rerror_rate 464948 94.1150274988
## 18 srv_rerror_rate 464320 93.9879073966
## 19 dst_host_srv_rerror_rate 459805 93.0739786365
## 20 srv_diff_host_rate 459377 92.9873426433
## 21 dst_host_rerror_rate 458792 92.8689266246
## 22 dst_host_srv_diff_host_rate 441889 89.4474121545
## 23 logged_in 420784 85.1753265549
## 24 dst_bytes 408258 82.6398068098
## 25 srv_serror_rate 405686 82.1191811684
## 26 serror_rate 404787 81.9372050986
## 27 dst_host_srv_serror_rate 400945 81.1595053652
## 28 dst_host_serror_rate 399810 80.9297580467
## 29 diff_srv_rate 382021 77.3288989739
## 30 dst_host_diff_srv_rate 347031 70.2462041087
## 31 dst_host_same_src_port_rate 142860 28.9177990409
## 32 src_bytes 115342 23.3475904870
## 33 dst_host_same_srv_rate 11469 2.3215612292
## 34 same_srv_rate 3627 0.7341793163
## 35 dst_host_count 3 0.0006072616
## 36 dst_host_srv_count 3 0.0006072616
## 37 count 2 0.0004048411
## 38 srv_count 2 0.0004048411
## 39 protocol_type 0 0.0000000000
## 40 service 0 0.0000000000
## 41 flag 0 0.0000000000
## 42 connection 0 0.0000000000
Conclusiones:
- Lo normal en muchos de estos parámetros es tener ceros, porque o bien son operaciones que se hacen poco o son flags.
- Las variables que son ratios (terminadas en _rate) es normal con un volumen muy alto de ceros. Como estas variables las vamos a discretizar, no plantean un problema.
- Las variables num_outbound_cmds y is_host_login tienen el 100% de los datos que son cero. Por tanto, son candidatas a ser eliminadas.
- Las variables urgent, su_attempted, land, num_shells, root_shell, num_failed_logins, num_file_creations, num_access_files tienen más del 99.9% de datos nulos. Pero estas variables miden los accesos con permisos de administrador o el numero de ficheros accedidos, o el numero de intentos de login, que son todos indicadores de un intento de intrusión. Por lo tanto, las vamos a mantener en el dataset a pesar del gran numero de ceros, porque tiene sentido que sea así. Para disminuir el impacto de tantos ceros, podemos discretizar estas variables.
2.5. Calidad de datos: análisis de atípicos
Dividimos el análisis entre las variables tipo numerico y tipo integer.
2.5.1. Variables tipo numerico
Podemos ver que todas las variables de tipo númerico con los ratios (_rate), y su valor más alto es siempre 1, por lo que puede ser un atípico.
out <- function(variable){
t(t(head(sort(variable,decreasing = T),20))) #la doble traspuesta es un truco para que se visualice la salida, si no lo que crearia es una coleccion de dataframes que no se ven bien
}
atipicos_variables_numeric <- lapply(df,function(x){
if(is.double(x)) out(x)
})
print(atipicos_variables_numeric)## $duration
## NULL
##
## $protocol_type
## NULL
##
## $service
## NULL
##
## $flag
## NULL
##
## $src_bytes
## NULL
##
## $dst_bytes
## NULL
##
## $land
## NULL
##
## $wrong_fragment
## NULL
##
## $urgent
## NULL
##
## $hot
## NULL
##
## $num_failed_logins
## NULL
##
## $logged_in
## NULL
##
## $num_compromised
## NULL
##
## $root_shell
## NULL
##
## $su_attempted
## NULL
##
## $num_root
## NULL
##
## $num_file_creations
## NULL
##
## $num_shells
## NULL
##
## $num_access_files
## NULL
##
## $num_outbound_cmds
## NULL
##
## $is_host_login
## NULL
##
## $is_guest_login
## NULL
##
## $count
## NULL
##
## $srv_count
## NULL
##
## $serror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $srv_serror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $rerror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $srv_rerror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $same_srv_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $diff_srv_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $srv_diff_host_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_count
## NULL
##
## $dst_host_srv_count
## NULL
##
## $dst_host_same_srv_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_diff_srv_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_same_src_port_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_srv_diff_host_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_serror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_srv_serror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_rerror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $dst_host_srv_rerror_rate
## [,1]
## [1,] 1
## [2,] 1
## [3,] 1
## [4,] 1
## [5,] 1
## [6,] 1
## [7,] 1
## [8,] 1
## [9,] 1
## [10,] 1
## [11,] 1
## [12,] 1
## [13,] 1
## [14,] 1
## [15,] 1
## [16,] 1
## [17,] 1
## [18,] 1
## [19,] 1
## [20,] 1
##
## $connection
## NULL
Conclusión: Estas variables hemos decidido discretizarlas, por los que estos atípicos no afectan.
2.5.2 - Analizamos las que son de tipo integer
out_int <- function(variable){
t(t(table(variable))) #la doble traspuesta es un truco para que se visualice la salida, si no lo que crearia es una coleccion de dataframes que no se ven bien
}
atipicos_variables_integer <- lapply(df,function(x){
if(is.integer(x)) out_int(x)
})
#hacemos algo más facil la visualización mostrando solo algunos datos de cada variable
for(i in 1:length(atipicos_variables_integer)){
print(names(atipicos_variables_integer[i]))
print(head(atipicos_variables_integer[[i]]))
cat("\n","----------------","\n","\n")
}## [1] "duration"
##
## variable [,1]
## 0 481671
## 1 2476
## 2 870
## 3 625
## 4 413
## 5 554
##
## ----------------
##
## [1] "protocol_type"
## NULL
##
## ----------------
##
## [1] "service"
## NULL
##
## ----------------
##
## [1] "flag"
## NULL
##
## ----------------
##
## [1] "src_bytes"
##
## variable [,1]
## 0 115342
## 1 257
## 4 4
## 5 12
## 6 67
## 7 104
##
## ----------------
##
## [1] "dst_bytes"
##
## variable [,1]
## 0 408258
## 1 5
## 4 107
## 5 2
## 6 1
## 14 1
##
## ----------------
##
## [1] "land"
##
## variable [,1]
## 0 493999
## 1 22
##
## ----------------
##
## [1] "wrong_fragment"
##
## variable [,1]
## 0 492783
## 1 268
## 3 970
##
## ----------------
##
## [1] "urgent"
##
## variable [,1]
## 0 494017
## 1 2
## 2 1
## 3 1
##
## ----------------
##
## [1] "hot"
##
## variable [,1]
## 0 490829
## 1 256
## 2 2192
## 3 38
## 4 112
## 5 51
##
## ----------------
##
## [1] "num_failed_logins"
##
## variable [,1]
## 0 493958
## 1 57
## 2 3
## 3 1
## 4 1
## 5 1
##
## ----------------
##
## [1] "logged_in"
##
## variable [,1]
## 0 420784
## 1 73237
##
## ----------------
##
## [1] "num_compromised"
##
## variable [,1]
## 0 491797
## 1 2151
## 2 24
## 3 11
## 4 16
## 5 2
##
## ----------------
##
## [1] "root_shell"
##
## variable [,1]
## 0 493966
## 1 55
##
## ----------------
##
## [1] "su_attempted"
##
## variable [,1]
## 0 494009
## 1 6
## 2 6
##
## ----------------
##
## [1] "num_root"
##
## variable [,1]
## 0 493436
## 1 233
## 2 22
## 3 3
## 4 10
## 5 12
##
## ----------------
##
## [1] "num_file_creations"
##
## variable [,1]
## 0 493756
## 1 207
## 2 36
## 4 7
## 5 1
## 7 1
##
## ----------------
##
## [1] "num_shells"
##
## variable [,1]
## 0 493970
## 1 48
## 2 3
##
## ----------------
##
## [1] "num_access_files"
##
## variable [,1]
## 0 493567
## 1 424
## 2 25
## 3 2
## 4 1
## 6 1
##
## ----------------
##
## [1] "num_outbound_cmds"
##
## variable [,1]
## 0 494021
##
## ----------------
##
## [1] "is_host_login"
##
## variable [,1]
## 0 494021
##
## ----------------
##
## [1] "is_guest_login"
##
## variable [,1]
## 0 493336
## 1 685
##
## ----------------
##
## [1] "count"
##
## variable [,1]
## 0 2
## 1 39214
## 2 11219
## 3 5812
## 4 5400
## 5 4469
##
## ----------------
##
## [1] "srv_count"
##
## variable [,1]
## 0 2
## 1 37001
## 2 18857
## 3 11280
## 4 9875
## 5 9160
##
## ----------------
##
## [1] "serror_rate"
## NULL
##
## ----------------
##
## [1] "srv_serror_rate"
## NULL
##
## ----------------
##
## [1] "rerror_rate"
## NULL
##
## ----------------
##
## [1] "srv_rerror_rate"
## NULL
##
## ----------------
##
## [1] "same_srv_rate"
## NULL
##
## ----------------
##
## [1] "diff_srv_rate"
## NULL
##
## ----------------
##
## [1] "srv_diff_host_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_count"
##
## variable [,1]
## 0 3
## 1 2884
## 2 2023
## 3 1434
## 4 1317
## 5 1073
##
## ----------------
##
## [1] "dst_host_srv_count"
##
## variable [,1]
## 0 3
## 1 11895
## 2 7243
## 3 5855
## 4 5382
## 5 5494
##
## ----------------
##
## [1] "dst_host_same_srv_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_diff_srv_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_same_src_port_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_srv_diff_host_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_serror_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_srv_serror_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_rerror_rate"
## NULL
##
## ----------------
##
## [1] "dst_host_srv_rerror_rate"
## NULL
##
## ----------------
##
## [1] "connection"
## NULL
##
## ----------------
##
Conclusión: todas estas variables tienen datos atipicos porque contienen muchos ceros, pero ya hemos dicho que las mantenemos porque es normal en este tipo de datos. Discretizaremos estas variables o las pasamos a factor para minimizar este efecto.
- Variables integer a discretizar: duration, src_bytes, dst_bytes.
- Variables integer a factor: wrong_fragment, urgent, hot, num_failed_logings, num_compromised, num_root, count, srv_count, dst_host_count, dst_host_srv_count.
2.6 - Analisis longitudinal
Analisis de coherencia de las variables dentro de si mismas.
longi <- df %>%
summarise_all(mean) %>% #calcular la media de cada variable
t() %>% #transponerlo para tenerlo en una sola columna y leerlo mejor
as.data.frame() #reconvertirlo a dataframe porque t() lo pasa a matriz
data.frame(variable = rownames(longi), media = longi$V1) %>% #crear un nuevo dataframe para poder ordenar por el nombre
arrange(desc(media)) #ordenar por el nombre para tener la vision longitudinal## variable media
## 1 src_bytes 3025.61029591859
## 2 dst_bytes 868.53242473498
## 3 count 332.28569028442
## 4 srv_count 292.90655660387
## 5 dst_host_count 232.47077755804
## 6 dst_host_srv_count 188.66567008285
## 7 duration 47.97930249929
## 8 same_srv_rate 0.79154734313
## 9 dst_host_same_srv_rate 0.75377969762
## 10 dst_host_same_src_port_rate 0.60193475581
## 11 dst_host_serror_rate 0.17675396390
## 12 serror_rate 0.17668665907
## 13 srv_serror_rate 0.17660880813
## 14 dst_host_srv_serror_rate 0.17644262086
## 15 logged_in 0.14824673445
## 16 dst_host_rerror_rate 0.05811761038
## 17 srv_rerror_rate 0.05771894312
## 18 rerror_rate 0.05743340870
## 19 dst_host_srv_rerror_rate 0.05741166873
## 20 hot 0.03451877552
## 21 dst_host_diff_srv_rate 0.03090573073
## 22 srv_diff_host_rate 0.02899680378
## 23 diff_srv_rate 0.02098238739
## 24 num_root 0.01135174416
## 25 num_compromised 0.01021211649
## 26 dst_host_srv_diff_host_rate 0.00668350131
## 27 wrong_fragment 0.00643292492
## 28 is_guest_login 0.00138658073
## 29 num_file_creations 0.00108294992
## 30 num_access_files 0.00100805431
## 31 num_failed_logins 0.00015181541
## 32 root_shell 0.00011133130
## 33 num_shells 0.00010930709
## 34 land 0.00004453252
## 35 su_attempted 0.00003643570
## 36 urgent 0.00001416944
## 37 num_outbound_cmds 0.00000000000
## 38 is_host_login 0.00000000000
## 39 protocol_type NA
## 40 service NA
## 41 flag NA
## 42 connection NA
No se detectan anomalias en las medias, salvo en las variables que ya hemos dicho que tienen muchos ceros como atípicos, y por tanto la media es muy cercana a cero. Al ser un comportamiento normal, dejamos estas variables porque contienen patrones claros de intrusión. Al discretizarlas, minimizamos este impacto.
2.7 - Analisis de coherencia
En este caso no tenemos variables que podamos contrastar unas contra otras, porque las posibles diferentes combinaciones pueden ser muy altas, para ver si presentan incoherencias.
2.8. Analisis de la variable target
En este proyecto debemos predecir la variable connection, y distinguir entre conexiones buenas y malas. Vamos a ver con un histograma, como se distribuye la variable target.
table(df$connection)##
## back buffer_overflow ftp_write guess_passwd
## 2203 30 8 53
## imap ipsweep land loadmodule
## 12 1247 21 9
## multihop neptune nmap normal
## 7 107201 231 97278
## perl phf pod portsweep
## 3 4 264 1040
## rootkit satan smurf spy
## 10 1589 280790 2
## teardrop warezclient warezmaster
## 979 1020 20
ggplot(df,aes(x = df$connection)) + geom_bar()Detectamos 23 tipos de trafico (back, buffer_overflow, ftp_write, guess_passwd, imap, ipsweep, land, loadmodule, multihop, neptune, nmap, normal, perl, phf, pod, portsweep, rootkit, satan, smurf, spy, teardrop, warezclient, warezmaster) que debemos de clasificar en bueno y malo, y crear así la variable target.
Según el The Third International Knowledge Discovery and Data Mining Tools Competition, creadores del dataset, estos tipos de tráfico son para los siguientes ataques:
- Normal es el trafico lícito.
- Ataques DoS (Denial of Service): smurf, neptune, back, teardrop, pod, land.
- Ataques R2L (Remote to Local): warezclient, guess_passwd, warezmaster, imap, ftp_write, multihop, phf, spy.
- Ataques U2R (User to Root): buffer_overflow, rootkit, loadmodule, perl.
Por lo tanto, debemos clasificar como trafico 0 (no intrusión) al trafico Normal, y el resto lo clasificaremos como 1 (intrusión).
2.9 - Acciones resultado del analisis de calidad de datos y exploratorio
Aquí ejecutamos todas las acciones que hemos ido anotando del análisis de la calidad de los datos.
Debemos realizar lo siguiente:
- Pasar a factores las siguientes variables: logged_in, su_attempted, is_guest_login, protocol_type, service, flag, wrong_fragment, urgent, hot, num_compromised, num_root.
- Discretizar las siguientes variables: serror_rate, srv_serror_rate, rerror_rate, srv_rerror_rate, same_srv_rate, diff_srv_rate, srv_diff_host_rate, dst_host_same_srv_rate, dst_host_diff_srv_rate, dst_host_same_src_port_rate, dst_host_srv_diff_host_rate, dst_host_serror_rate, dst_host_srv_serror_rate, dst_host_rerror_rate, dst_host_srv_rerror_rate, land, num_shells, root_shell, num_failed_logins, num_file_creations, num_access_files, duration, src_bytes, dst_bytes, count, srv_count, dst_host_count, dst_host_srv_count.
- Eliminar las siguientes variables: num_outbound_cmds, is_host_login.
- Clasificar la variable target connection entre trafico normal (Normal) e intrusión (el resto).
a_factores <- c("logged_in", "su_attempted", "is_guest_login", "protocol_type", "service", "flag", "wrong_fragment", "urgent", "hot", "num_compromised", "num_root", "connection","intrusion")
df_backup <- df #hacemos un backup del dataframe original
df <- df %>%
mutate(intrusion = ifelse(connection == "normal",0,1)) %>%
mutate_at(a_factores,funs(factor))
df <- select(df,-"num_outbound_cmds",-"is_host_login",-"connection") #eliminamos las variables
print(head(df))## duration protocol_type service flag src_bytes dst_bytes land
## 1 0 tcp http SF 181 5450 0
## 2 0 tcp http SF 239 486 0
## 3 0 tcp http SF 235 1337 0
## 4 0 tcp http SF 219 1337 0
## 5 0 tcp http SF 217 2032 0
## 6 0 tcp http SF 217 2032 0
## wrong_fragment urgent hot num_failed_logins logged_in num_compromised
## 1 0 0 0 0 1 0
## 2 0 0 0 0 1 0
## 3 0 0 0 0 1 0
## 4 0 0 0 0 1 0
## 5 0 0 0 0 1 0
## 6 0 0 0 0 1 0
## root_shell su_attempted num_root num_file_creations num_shells
## 1 0 0 0 0 0
## 2 0 0 0 0 0
## 3 0 0 0 0 0
## 4 0 0 0 0 0
## 5 0 0 0 0 0
## 6 0 0 0 0 0
## num_access_files is_guest_login count srv_count serror_rate
## 1 0 0 8 8 0
## 2 0 0 8 8 0
## 3 0 0 8 8 0
## 4 0 0 6 6 0
## 5 0 0 6 6 0
## 6 0 0 6 6 0
## srv_serror_rate rerror_rate srv_rerror_rate same_srv_rate diff_srv_rate
## 1 0 0 0 1 0
## 2 0 0 0 1 0
## 3 0 0 0 1 0
## 4 0 0 0 1 0
## 5 0 0 0 1 0
## 6 0 0 0 1 0
## srv_diff_host_rate dst_host_count dst_host_srv_count
## 1 0 9 9
## 2 0 19 19
## 3 0 29 29
## 4 0 39 39
## 5 0 49 49
## 6 0 59 59
## dst_host_same_srv_rate dst_host_diff_srv_rate
## 1 1 0
## 2 1 0
## 3 1 0
## 4 1 0
## 5 1 0
## 6 1 0
## dst_host_same_src_port_rate dst_host_srv_diff_host_rate
## 1 0.11 0
## 2 0.05 0
## 3 0.03 0
## 4 0.03 0
## 5 0.02 0
## 6 0.02 0
## dst_host_serror_rate dst_host_srv_serror_rate dst_host_rerror_rate
## 1 0 0 0
## 2 0 0 0
## 3 0 0 0
## 4 0 0 0
## 5 0 0 0
## 6 0 0 0
## dst_host_srv_rerror_rate intrusion
## 1 0 0
## 2 0 0
## 3 0 0
## 4 0 0
## 5 0 0
## 6 0 0
Vamos a revisar ahora los niveles de las variables que hemos pasado a factores, para comprobar que no hay niveles raros que debamos de revisar.
for(i in 1:length(names(df))) {
if(is.factor(df[[i]])) {
cat("\n")
cat("Variable -",names(df)[i],"\n")
cat("Clase -",class(df[[i]]),"\n")
niveles <- levels(df[[i]])
cat("Niveles -",niveles,"\n")
print("-------------------------------")
}
}##
## Variable - protocol_type
## Clase - factor
## Niveles - icmp tcp udp
## [1] "-------------------------------"
##
## Variable - service
## Clase - factor
## Niveles - auth bgp courier csnet_ns ctf daytime discard domain domain_u echo eco_i ecr_i efs exec finger ftp ftp_data gopher hostnames http http_443 imap4 IRC iso_tsap klogin kshell ldap link login mtp name netbios_dgm netbios_ns netbios_ssn netstat nnsp nntp ntp_u other pm_dump pop_2 pop_3 printer private red_i remote_job rje shell smtp sql_net ssh sunrpc supdup systat telnet tftp_u tim_i time urh_i urp_i uucp uucp_path vmnet whois X11 Z39_50
## [1] "-------------------------------"
##
## Variable - flag
## Clase - factor
## Niveles - OTH REJ RSTO RSTOS0 RSTR S0 S1 S2 S3 SF SH
## [1] "-------------------------------"
##
## Variable - wrong_fragment
## Clase - factor
## Niveles - 0 1 3
## [1] "-------------------------------"
##
## Variable - urgent
## Clase - factor
## Niveles - 0 1 2 3
## [1] "-------------------------------"
##
## Variable - hot
## Clase - factor
## Niveles - 0 1 2 3 4 5 6 7 9 10 12 14 15 16 17 18 19 20 22 24 28 30
## [1] "-------------------------------"
##
## Variable - logged_in
## Clase - factor
## Niveles - 0 1
## [1] "-------------------------------"
##
## Variable - num_compromised
## Clase - factor
## Niveles - 0 1 2 3 4 5 6 7 9 11 12 13 16 18 21 22 38 102 238 275 281 767 884
## [1] "-------------------------------"
##
## Variable - su_attempted
## Clase - factor
## Niveles - 0 1 2
## [1] "-------------------------------"
##
## Variable - num_root
## Clase - factor
## Niveles - 0 1 2 3 4 5 6 7 9 12 14 16 39 54 119 268 278 306 857 993
## [1] "-------------------------------"
##
## Variable - is_guest_login
## Clase - factor
## Niveles - 0 1
## [1] "-------------------------------"
##
## Variable - intrusion
## Clase - factor
## Niveles - 0 1
## [1] "-------------------------------"
Vemos que la variable service tiene muchos niveles, en concreto auth , bgp , courier , csnet_ns , ctf , daytime , discard , domain , domain_u , echo , eco_i , ecr_i , efs , exec , finger , ftp , ftp_data , gopher , hostnames , http , http_443 , imap4 , IRC , iso_tsap , klogin , kshell , ldap , link , login , mtp , name , netbios_dgm, netbios_ns , netbios_ssn, netstat , nnsp , nntp , ntp_u , other , pm_dump , pop_2 , pop_3 , printer , private , red_i , remote_job , rje , shell , smtp , sql_net , ssh , sunrpc , supdup , systat , telnet , tftp_u , tim_i , time , urh_i , urp_i , uucp , uucp_path , vmnet , whois , X11 , Z39_50 . RandomForest, que utilizaremos tanto para la preselección de variables asi como modelo de propensión, lo máximo que puede manejar son variables categóricas con 53 niveles. Por tanto, para poder preseleccionar las variables con RandomForest en el siguiente apartado, debemos discretizar la variable service aunque ya sea un factor.
Para discretizar la variable service vamos a agrupar los servicios por tipo de servicio, por ejemplo, dominio, remote, network_topology… intentando hacer agrupaciones que tengan sentido desde el punto de visto de querer hacer una intrusión (por ejemplo, un abuso de servicios de dominio o de network_topology puede indicar un posible ataque DoS o de fuerza bruta). Las agrupaciones que hacemos son las 12 siguientes:
- domain: auth, name, domain, domain_u, hostnames, ldap, netbios_ssn, netbios_dgm, netbios_ns.
- isp: bgp, courier, mtp, iso_tsap, nnsp, uucp, uucp_path, red_i, tim_i, urh_i, urp_i, eco_i, ecr_i.
- csnet_ns: csnet_ns.
- ctf: ctf.
- time: daytime, ntp_u, time.
- discard: discard.
- pm_dump: pm_dump.
- network: netstat, echo, whois, finger, link, systat.
- remote_exec: exec, remote_job, rje, ssh, shell, kshell, telnet, klogin, login, sunrpc, X11, supdup.
- other: other.
- private: private.
- user_services: vmnet, Z39_50, tftp_u, efs, ftp, nntp, ftp_data, pop_2, pop_3, gopher, http, printer, http_443, smtp, sql_net, imap, IRC.
df <- df %>% mutate(service_disc = as.factor(case_when(
(service == "auth") | (service == "name") | (service == "domain") | (service == "domain_u") | (service == "hostnames") | (service == "ldap") | (service == "netbios_ssn") | (service == "netbios_dgm") | (service == "netbios_ns") ~ '09_domain',
(service == "bgp") | (service == "courier") | (service == "mtp") | (service == "iso_tsap") | (service == "nnsp") | (service == "uucp") | (service == "uucp_path") | (service == "red_i") | (service == "tim_i") | (service == "urh_i") | (service == "urp_i") | (service == "eco_i") | (service == "ecr_i") ~ '12_isp',
(service == "csnet_ns") ~ '02_csnet_ns',
(service == "ctf") ~ '03_ctf',
(service == "daytime") | (service == "ntp_u") | (service == "time") ~ '05_time',
(service == "discard") ~ '04_discard',
(service == "pm_dump") ~ '01_pm_dump',
(service == "netstat") | (service == "echo") | (service == "whois") | (service == "finger") | (service == "link") | (service == "systat") ~ '06_network',
(service == "exec") | (service == "remote_job") | (service == "rje") | (service == "ssh") | (service == "shell") | (service == "kshell") | (service == "telnet") | (service == "klogin") | (service == "login") | (service == "sunrpc") | (service == "X11") | (service == "supdup") ~ '07_remote_exec',
(service == "other") ~ '08_other',
(service == "private") ~ '11_private',
(service == "vmnet") | (service == "Z39_50") | (service == "tftp_u") | (service == "efs") | (service == "ftp") | (service == "nntp") | (service == "ftp_data") | (service == "pop_2") | (service == "pop_3") | (service == "gopher") | (service == "http") | (service == "printer") | (service == "http_443") | (service == "smtp") | (service == "sql_net") | (service == "imap4") | (service == "IRC") ~ '10_user_services',
TRUE ~ '00_ERROR'))
)
#vemos la distribución, nos aseguramos que no tenemos ningún caso en error
table(df$service_disc) ##
## 01_pm_dump 02_csnet_ns 03_ctf 04_discard
## 1 126 97 116
## 05_time 06_network 07_remote_exec 08_other
## 640 1204 1591 7237
## 09_domain 10_user_services 11_private 12_isp
## 6918 80843 110893 284355
ggplot(df,aes(x = service_disc)) + geom_bar()df <- select(df,-"service") #eliminamos la variable service3 - Trasformación de datos
3.1 - Creacion de la variable target
Ya hemos transformado la variable target en una variable dicotómica con valor 0 para tráfico normal y valor 1 para trafico intrusivo. Por lo tanto, no hay nada más que hacer en este punto.
3.2 - Preparacion de las variables independientes
3.2.1 - Preseleccion de variables independientes
Creamos una lista larga con todas las variables independientes. En este punto, no descartaremos ninguna variable. Al no tener variables históricas ni tampoco un código númerico para identificar cada observación, no tenemos por que deseleccionar ninguna. Solo eliminamos la variable target.
ind_larga<-names(df) #lista con todas las variables
target <- c('intrusion') #identificamos las que no queremos usar
ind_larga <- setdiff(ind_larga,target)Creamos una muestra m menor para que los calculos sean mas rapidos. Reducir la muestra no restará eficacia a los algoritmos de preselección. El tamaño de la muestra se determina para que, estadisticamente, sea relevante. Seleccionaremos 30000 registros, que son estadísticamente relevante con un error muy bajo.
set.seed(12345)
m <- sample_n(df,30000)3.2.1.1 - Preseleccion con RandomForest
Vamos ha hacer la primera preselección con RandomForest. Este es el ranking según randomForest:
pre_rf <- randomForest(formula = reformulate(ind_larga,target),data= m,mtry=2,ntree=50, importance = T)
imp_rf <- importance(pre_rf)[,4] #como importance devuelve una matriz con varias metricas, tenemos que extraer asi el decrecimiento en Gini (metrica 4) que es el que mas nos interesa, porque la experiencia demuestra que funciona bien, pero también se pueden probar otras metricas de las que devuelve en la matriz de importancia, o una combinación de ambas
imp_rf <- data.frame(VARIABLE = names(imp_rf), IMP_RF = imp_rf) #lo transformamos a dataframe
imp_rf <- imp_rf %>% arrange(desc(IMP_RF)) %>% mutate(RANKING_RF = 1:nrow(imp_rf)) #creamos el ranking
imp_rf## VARIABLE IMP_RF RANKING_RF
## 1 count 1343.996480637188 1
## 2 service_disc 1136.209518300470 2
## 3 logged_in 963.380272061798 3
## 4 protocol_type 716.584691075042 4
## 5 src_bytes 702.652476510291 5
## 6 srv_count 579.776944605514 6
## 7 dst_bytes 430.193676471988 7
## 8 diff_srv_rate 367.161345261635 8
## 9 dst_host_srv_diff_host_rate 355.346583829165 9
## 10 dst_host_diff_srv_rate 289.972253264370 10
## 11 srv_diff_host_rate 288.601992580653 11
## 12 dst_host_same_src_port_rate 259.202325808515 12
## 13 dst_host_count 160.944174211661 13
## 14 dst_host_srv_count 155.752311680359 14
## 15 flag 153.115709089470 15
## 16 dst_host_serror_rate 129.273432165919 16
## 17 dst_host_same_srv_rate 88.580355047842 17
## 18 duration 79.506399369129 18
## 19 serror_rate 74.289992143068 19
## 20 same_srv_rate 67.353375243649 20
## 21 dst_host_srv_serror_rate 59.129379561908 21
## 22 dst_host_rerror_rate 45.366984666177 22
## 23 srv_rerror_rate 44.740719226242 23
## 24 rerror_rate 32.402845729795 24
## 25 num_compromised 29.905409038280 25
## 26 dst_host_srv_rerror_rate 28.788441622916 26
## 27 hot 28.253908147392 27
## 28 srv_serror_rate 26.019969534592 28
## 29 wrong_fragment 11.241678399234 29
## 30 is_guest_login 4.251423613215 30
## 31 num_access_files 2.015388746656 31
## 32 num_root 1.618265729562 32
## 33 num_failed_logins 0.411184062311 33
## 34 num_file_creations 0.357678804735 34
## 35 root_shell 0.329594856529 35
## 36 num_shells 0.000002366789 36
## 37 land 0.000000000000 37
## 38 urgent 0.000000000000 38
## 39 su_attempted 0.000000000000 39
3.2.1.2 - Preseleccion con Information Value
La función de IV necesita que la variable target sea numerica. Como nosotros la tenemos como factor, creamos un dataset diferente y le cambiamos la valiable target de factor a numeric. Este es el ranking según IV:
imp_iv## VARIABLE IMP_IV RANKING_IV
## 1 dst_bytes 7.1405 1
## 2 count 4.5443 2
## 3 dst_host_same_src_port_rate 4.5146 3
## 4 protocol_type 4.2697 4
## 5 logged_in 4.2003 5
## 6 dst_host_count 3.0241 6
## 7 dst_host_srv_diff_host_rate 2.7208 7
## 8 srv_diff_host_rate 1.6720 8
## 9 dst_host_srv_serror_rate 1.6110 9
## 10 dst_host_serror_rate 1.6077 10
## 11 same_srv_rate 1.5727 11
## 12 dst_host_same_srv_rate 1.5420 12
## 13 flag 1.4665 13
## 14 dst_host_srv_count 1.4278 14
## 15 serror_rate 1.3706 15
## 16 srv_serror_rate 1.3684 16
## 17 diff_srv_rate 1.3003 17
## 18 srv_count 0.8788 18
## 19 dst_host_diff_srv_rate 0.4535 19
## 20 num_compromised 0.0106 20
## 21 is_guest_login 0.0022 21
## 22 wrong_fragment 0.0000 22
## 23 num_root 0.0000 23
## 24 duration NA 24
## 25 src_bytes NA 25
## 26 land NA 26
## 27 urgent NA 27
## 28 hot NA 28
## 29 num_failed_logins NA 29
## 30 root_shell NA 30
## 31 su_attempted NA 31
## 32 num_file_creations NA 32
## 33 num_shells NA 33
## 34 num_access_files NA 34
## 35 rerror_rate NA 35
## 36 srv_rerror_rate NA 36
## 37 dst_host_rerror_rate NA 37
## 38 dst_host_srv_rerror_rate NA 38
## 39 service_disc NA 39
3.2.1.3 - Preseleccion final
En este punto ya tenemos dos metodologías distintas con las que hemos identificado a las variables que van a ser más predictivas.Lo que vamos a hacer es ver cómo las podemos unificar y obtener un criterio unificado para estar muy seguros de que estamos seleccionando las variables correctas. Esta es la lista de importancia según la combinación de ambos metodos:
imp_final <- inner_join(imp_rf,imp_iv,by='VARIABLE') %>%
select(VARIABLE,IMP_RF,IMP_IV,RANKING_RF,RANKING_IV) %>% #ponerlos en orden mas legible
mutate(RANKING_TOT = RANKING_RF + RANKING_IV) %>%
arrange(RANKING_TOT)
imp_final## VARIABLE IMP_RF IMP_IV RANKING_RF
## 1 count 1343.996480637188 4.5443 1
## 2 logged_in 963.380272061798 4.2003 3
## 3 protocol_type 716.584691075042 4.2697 4
## 4 dst_bytes 430.193676471988 7.1405 7
## 5 dst_host_same_src_port_rate 259.202325808515 4.5146 12
## 6 dst_host_srv_diff_host_rate 355.346583829165 2.7208 9
## 7 srv_diff_host_rate 288.601992580653 1.6720 11
## 8 dst_host_count 160.944174211661 3.0241 13
## 9 srv_count 579.776944605514 0.8788 6
## 10 diff_srv_rate 367.161345261635 1.3003 8
## 11 dst_host_serror_rate 129.273432165919 1.6077 16
## 12 dst_host_srv_count 155.752311680359 1.4278 14
## 13 flag 153.115709089470 1.4665 15
## 14 dst_host_diff_srv_rate 289.972253264370 0.4535 10
## 15 dst_host_same_srv_rate 88.580355047842 1.5420 17
## 16 src_bytes 702.652476510291 NA 5
## 17 dst_host_srv_serror_rate 59.129379561908 1.6110 21
## 18 same_srv_rate 67.353375243649 1.5727 20
## 19 serror_rate 74.289992143068 1.3706 19
## 20 service_disc 1136.209518300470 NA 2
## 21 duration 79.506399369129 NA 18
## 22 srv_serror_rate 26.019969534592 1.3684 28
## 23 num_compromised 29.905409038280 0.0106 25
## 24 wrong_fragment 11.241678399234 0.0000 29
## 25 is_guest_login 4.251423613215 0.0022 30
## 26 hot 28.253908147392 NA 27
## 27 num_root 1.618265729562 0.0000 32
## 28 dst_host_rerror_rate 45.366984666177 NA 22
## 29 srv_rerror_rate 44.740719226242 NA 23
## 30 rerror_rate 32.402845729795 NA 24
## 31 num_failed_logins 0.411184062311 NA 33
## 32 land 0.000000000000 NA 37
## 33 dst_host_srv_rerror_rate 28.788441622916 NA 26
## 34 num_access_files 2.015388746656 NA 31
## 35 root_shell 0.329594856529 NA 35
## 36 urgent 0.000000000000 NA 38
## 37 num_file_creations 0.357678804735 NA 34
## 38 num_shells 0.000002366789 NA 36
## 39 su_attempted 0.000000000000 NA 39
## RANKING_IV RANKING_TOT
## 1 2 3
## 2 5 8
## 3 4 8
## 4 1 8
## 5 3 15
## 6 7 16
## 7 8 19
## 8 6 19
## 9 18 24
## 10 17 25
## 11 10 26
## 12 14 28
## 13 13 28
## 14 19 29
## 15 12 29
## 16 25 30
## 17 9 30
## 18 11 31
## 19 15 34
## 20 39 41
## 21 24 42
## 22 16 44
## 23 20 45
## 24 22 51
## 25 21 51
## 26 28 55
## 27 23 55
## 28 37 59
## 29 36 59
## 30 35 59
## 31 29 62
## 32 26 63
## 33 38 64
## 34 34 65
## 35 30 65
## 36 27 65
## 37 32 66
## 38 33 69
## 39 31 70
¿Son los metodos fiables? Si los resultados fueran muy diferentes, podrían que ambos metodos se anularan y la clasificación final no se corresponde con el nivel de predicción de la variable target. Vamos a hacer una correlacion entre ellos a ver si dan cosas similares.
cor(imp_final$IMP_RF,imp_final$IMP_IV,use = 'complete.obs')## [1] 0.6151697
Si nos dan fiabilidad los resultados ya que la correlacion es normal y por tanto los resultados de ambos valores se refuerzan.
ind_corta <- imp_final %>%
filter(RANKING_RF <= 18 | RANKING_IV <= 18) %>%
select(VARIABLE) #nos quedamos solo con el nombre
ind_corta <- as.character(ind_corta$VARIABLE) #lo pasamos a un vector en vez de un dataframeDecision: vamos a descartar aquellas variables que no hayan salido entre las 18 mas importantes en ninguno de los dos sistemas de seleccion de variables. Con esto quedan fuera las variables land, wrong_fragment, urgent, hot, num_failed_logins, num_compromised, root_shell, su_attempted, num_root, num_file_creations, num_shells, num_access_files, is_guest_login, rerror_rate, srv_rerror_rate, dst_host_rerror_rate y dst_host_srv_rerror_rate. Estas son las variables predictoras con las que vamos a trabajar finalmente. Son 22 variables.
ind_corta## [1] "count" "logged_in"
## [3] "protocol_type" "dst_bytes"
## [5] "dst_host_same_src_port_rate" "dst_host_srv_diff_host_rate"
## [7] "srv_diff_host_rate" "dst_host_count"
## [9] "srv_count" "diff_srv_rate"
## [11] "dst_host_serror_rate" "dst_host_srv_count"
## [13] "flag" "dst_host_diff_srv_rate"
## [15] "dst_host_same_srv_rate" "src_bytes"
## [17] "dst_host_srv_serror_rate" "same_srv_rate"
## [19] "serror_rate" "service_disc"
## [21] "duration" "srv_serror_rate"
3.2.2 - Seleccionar la lista de variables finales del proyecto
Solo tenemos que unir la variable target, que quitamos al inicio.
finales <- union(ind_corta,c('intrusion'))#creamos la lista de variables finales, incluyendo las que no tienen historia y la target
finales## [1] "count" "logged_in"
## [3] "protocol_type" "dst_bytes"
## [5] "dst_host_same_src_port_rate" "dst_host_srv_diff_host_rate"
## [7] "srv_diff_host_rate" "dst_host_count"
## [9] "srv_count" "diff_srv_rate"
## [11] "dst_host_serror_rate" "dst_host_srv_count"
## [13] "flag" "dst_host_diff_srv_rate"
## [15] "dst_host_same_srv_rate" "src_bytes"
## [17] "dst_host_srv_serror_rate" "same_srv_rate"
## [19] "serror_rate" "service_disc"
## [21] "duration" "srv_serror_rate"
## [23] "intrusion"
3.3 - Fichero final y limpieza del entorno
3.3.1 - Fichero final
Generamos el dataset final con las variables seleccionadas.
df <- df %>%
select(one_of(finales))3.3.2 - Limpieza del entorno
Durante todo el proceso anterior hemos creado muchas variables y ficheros temporales, vamos a aprovechar para limpiarlo todo y dejarlo organizado antes de pasar a la siguiente fase. Antes de la limpieza tenemos estos objetos:
ls() #Vemos todo lo que tenemos cargado en el entorno## [1] "a_factores" "atipicos_variables_integer"
## [3] "atipicos_variables_numeric" "contar_ceros"
## [5] "df" "df_backup"
## [7] "est_basicos" "finales"
## [9] "i" "imp_final"
## [11] "imp_iv" "imp_rf"
## [13] "ind_corta" "ind_larga"
## [15] "instalados" "longi"
## [17] "m" "m2"
## [19] "niveles" "num_ceros"
## [21] "num_nulos" "out"
## [23] "out_int" "paquetes"
## [25] "pre_rf" "target"
rm(list=setdiff(ls(),c('df','contar_ceros','target','a_factores'))) #borramos todo excepto el nuevo df
# y vamos a dejar preparadas unas variables que nos van a facilitar cosas en el futuro:
indep <- setdiff(names(df),target)Y despues de la limpieza, quedan estos:
ls()## [1] "a_factores" "contar_ceros" "df" "indep"
## [5] "target"
Vamos a guardar un cache temporal de datos, de forma que cuando queramos seguir trabajando desde aqui no tengamos que volver a ejecutar todo
saveRDS(df,'cache1_dataset_preseleccion.rds')4 - Creacion de variables sinteticas
Cargamos el cache que hemos grabado de la fase anterior, y volvemos a crear la variable target y el conjunto de variables independientes:
df <- readRDS(file = 'cache1_dataset_preseleccion.rds')4.1 - Creacion de variables sinteticas
No tenemos datos históricos, asi que no tiene sentido crear variables de tenencia, contratación, cancelación, medias o tendencias. Solo tenemos una foto estática con la que tenemos que predecir la variable target intrusion.
Aparte de la discretización de las variables continuas, que haremos más adelante, podemos intentar combinar algunas de las variables categoricas que tenemos, y que tenga sentido que estén relacionadas. Las variables que tenemos y sus niveles son los siguientes:
for(i in 1:length(indep)) {
if(is.factor(df[[i]])) {
cat("Variable -",indep[i],"\n")
cat("Clase -",class(df[[i]]),"\n")
niveles <- levels(df[[i]])
cat("Niveles -",niveles,"\n")
print("-------------------------------")
}
}## Variable - logged_in
## Clase - factor
## Niveles - 0 1
## [1] "-------------------------------"
## Variable - protocol_type
## Clase - factor
## Niveles - icmp tcp udp
## [1] "-------------------------------"
## Variable - flag
## Clase - factor
## Niveles - OTH REJ RSTO RSTOS0 RSTR S0 S1 S2 S3 SF SH
## [1] "-------------------------------"
## Variable - service_disc
## Clase - factor
## Niveles - 01_pm_dump 02_csnet_ns 03_ctf 04_discard 05_time 06_network 07_remote_exec 08_other 09_domain 10_user_services 11_private 12_isp
## [1] "-------------------------------"
La variable logged_in no se relaciona con ninguna de las otras 3 variables categóricas. La variable service_disc tambpoco tiene relación con las otras. Solo habría posibilidad de combinar protocol_type y flag. Vamos a ver como están relacionadas:
table(df$protocol_type,df$flag)##
## OTH REJ RSTO RSTOS0 RSTR S0 S1 S2 S3
## icmp 0 0 0 0 0 0 0 0 0
## tcp 8 26875 579 11 903 87007 57 24 10
## udp 0 0 0 0 0 0 0 0 0
##
## SF SH
## icmp 283602 0
## tcp 74484 107
## udp 20354 0
ggplot(df,aes(protocol_type,flag)) + geom_count()Vemos que los protocolos ICMP y UDP, que no establecen conexión, solo tienen el flag SF, asi que para esos dos protocolos podemos simplificar la categoria. Para el protocolo TCP, que si establece conexión entre origen y destino del paquete de datos, hay otros estados. Los que empiezan por RSXX se refieren al reseteo de la conexión, los que empiezan por SX al proceso de sincronización. Por lo tanto, para el protocolo TCP podemos establecer 4 categorias: tcp_OTH, tcp_REJ, tcp_RS, tcp_S, agrupando las correspondientes categorias de flag y protocol_type. Asi simplificamos estas dos variables relacionadas y la sustituimos por otra con menos niveles. El resultado es el siguiente:
df_backup <- df
df <- df %>% mutate(protocol_disc = as.factor(case_when(
protocol_type == "udp" & flag == "SF" ~ '01_udp_SF',
protocol_type == "icmp" & flag == "SF" ~ '02_icmp_SF',
protocol_type == "tcp" & flag == "OTH" ~ '03_tcp_OTH',
protocol_type == "tcp" & flag == "REJ" ~ '04_tcp_REJ',
(protocol_type == "tcp") & (flag == "RSTO" | flag == "RSTOS0" | flag == "RSTR") ~ '05_tcp_RS',
(protocol_type == "tcp") & (flag == "S0" | flag == "S1" | flag == "S2" | flag == "S3" | flag == "SF" | flag == "SH") ~ '06_tcp_S',
TRUE ~ '00_ERROR'))
)
#vemos la distribución, nos aseguramos que no tenemos ningún caso en error
table(df$protocol_disc) ##
## 01_udp_SF 02_icmp_SF 03_tcp_OTH 04_tcp_REJ 05_tcp_RS 06_tcp_S
## 20354 283602 8 26875 1493 161689
ggplot(df,aes(protocol_disc)) + geom_bar()#eliminamos las variables originales
df <- select(df, -protocol_type)
df <- select(df, -flag)
rm(list=c('i','niveles'))4.2. Discretización
4.2.1. Discretización automática
Primero vamos a crear la funcion que va a discretizar de forma automatica maximizando la capacidad predictiva de la nueva variable. Ademas, como vamos a usar en la modelizacion un algoritmo lineal, que es la regresion logistica, vamos a intentar que la discretizacion sea monotonica.
Las variables que tenemos para discretizar en nuestro dataset son las variables numéricas.
discretizar <- function(vi,target){
temp_df <- data.frame(vi = vi, target = target)
#smbinning necesita que la target sea numerica
temp_df$target <- as.numeric(as.character(temp_df$target))
disc <- smbinning(temp_df, y = 'target', x = 'vi')
return(disc)
}Hacemos las discretizaciones de las variables numericas.
#count:
disc_temp_count <- discretizar(df$count,df$intrusion)
df_temp <- select(df,count,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_count,chrname = 'count_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-count)
#dst_bytes:
disc_temp_dst_bytes <- discretizar(df$dst_bytes,df$intrusion)
df_temp <- select(df,dst_bytes,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_bytes,chrname = 'dst_bytes_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_bytes)
#dst_host_same_src_port_rate:
disc_temp_dst_host_same_src_port_rate <- discretizar(df$dst_host_same_src_port_rate,df$intrusion)
df_temp <- select(df,dst_host_same_src_port_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_same_src_port_rate,chrname = 'dst_host_same_src_port_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_same_src_port_rate)
#dst_host_srv_diff_host_rate:
disc_temp_dst_host_srv_diff_host_rate <- discretizar(df$dst_host_srv_diff_host_rate,df$intrusion)
df_temp <- select(df,dst_host_srv_diff_host_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_srv_diff_host_rate,chrname = 'dst_host_srv_diff_host_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_srv_diff_host_rate)
#srv_diff_host_rate:
disc_temp_srv_diff_host_rate <- discretizar(df$srv_diff_host_rate,df$intrusion)
df_temp <- select(df,srv_diff_host_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_srv_diff_host_rate,chrname = 'srv_diff_host_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-srv_diff_host_rate)
#dst_host_count:
disc_temp_dst_host_count <- discretizar(df$dst_host_count,df$intrusion)
df_temp <- select(df,dst_host_count,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_count,chrname = 'dst_host_count_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_count)
#srv_count:
disc_temp_srv_count <- discretizar(df$srv_count,df$intrusion)
df_temp <- select(df,srv_count,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_srv_count,chrname = 'srv_count_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-srv_count)
#diff_srv_rate:
disc_temp_diff_srv_rate <- discretizar(df$diff_srv_rate,df$intrusion)
df_temp <- select(df,diff_srv_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_diff_srv_rate,chrname = 'diff_srv_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-diff_srv_rate)
#dst_host_serror_rate:
disc_temp_dst_host_serror_rate <- discretizar(df$dst_host_serror_rate,df$intrusion)
df_temp <- select(df,dst_host_serror_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_serror_rate,chrname = 'dst_host_serror_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_serror_rate)
#dst_host_srv_count:
disc_temp_dst_host_srv_count <- discretizar(df$dst_host_srv_count,df$intrusion)
df_temp <- select(df,dst_host_srv_count,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_srv_count,chrname = 'dst_host_srv_count_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_srv_count)
#dst_host_diff_srv_rate:
disc_temp_dst_host_diff_srv_rate <- discretizar(df$dst_host_diff_srv_rate,df$intrusion)
df_temp <- select(df,dst_host_diff_srv_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_diff_srv_rate,chrname = 'dst_host_diff_srv_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_diff_srv_rate)
#dst_host_same_srv_rate:
disc_temp_dst_host_same_srv_rate <- discretizar(df$dst_host_same_srv_rate,df$intrusion)
df_temp <- select(df,dst_host_same_srv_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_same_srv_rate,chrname = 'dst_host_same_srv_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_same_srv_rate)
#dst_host_srv_serror_rate:
disc_temp_dst_host_srv_serror_rate <- discretizar(df$dst_host_srv_serror_rate,df$intrusion)
df_temp <- select(df,dst_host_srv_serror_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_dst_host_srv_serror_rate,chrname = 'dst_host_srv_serror_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-dst_host_srv_serror_rate)
#same_srv_rate:
disc_temp_same_srv_rate <- discretizar(df$same_srv_rate,df$intrusion)
df_temp <- select(df,same_srv_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_same_srv_rate,chrname = 'same_srv_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-same_srv_rate)
#serror_rate:
disc_temp_serror_rate <- discretizar(df$serror_rate,df$intrusion)
df_temp <- select(df,serror_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_serror_rate,chrname = 'serror_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-serror_rate)
#srv_serror_rate:
disc_temp_srv_serror_rate <- discretizar(df$srv_serror_rate,df$intrusion)
df_temp <- select(df,srv_serror_rate,intrusion) #creamos este temporal porque smbinning.gen necesita que el df tenga el mismo numero de columnas que la salida de la funcion discretizar
df_temp <- smbinning.gen(df_temp,disc_temp_srv_serror_rate,chrname = 'srv_serror_rate_disc')
#Metemos en df la nueva variable discretizada y eliminamos la original
df <- cbind(df,df_temp[3]) %>% select(-srv_serror_rate)El dataframe queda de la siguiente manera:
glimpse(df)## Observations: 494,021
## Variables: 22
## $ logged_in <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ src_bytes <int> 181, 239, 235, 219, 217, 217,...
## $ service_disc <fct> 10_user_services, 10_user_ser...
## $ duration <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ intrusion <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ protocol_disc <fct> 06_tcp_S, 06_tcp_S, 06_tcp_S,...
## $ count_disc <fct> 03 <= 47, 03 <= 47, 03 <= 47,...
## $ dst_bytes_disc <fct> 03 > 2416, 02 <= 2416, 02 <= ...
## $ dst_host_same_src_port_rate_disc <fct> 03 <= 0.99, 03 <= 0.99, 02 <=...
## $ dst_host_srv_diff_host_rate_disc <fct> 01 <= 0, 01 <= 0, 01 <= 0, 01...
## $ srv_diff_host_rate_disc <fct> 01 <= 0, 01 <= 0, 01 <= 0, 01...
## $ dst_host_count_disc <fct> 01 <= 33, 01 <= 33, 01 <= 33,...
## $ srv_count_disc <fct> 03 <= 9, 03 <= 9, 03 <= 9, 03...
## $ diff_srv_rate_disc <fct> 01 <= 0.03, 01 <= 0.03, 01 <=...
## $ dst_host_serror_rate_disc <fct> 01 <= 0.97, 01 <= 0.97, 01 <=...
## $ dst_host_srv_count_disc <fct> 02 <= 9, 04 <= 25, 05 <= 254,...
## $ dst_host_diff_srv_rate_disc <fct> 01 <= 0.04, 01 <= 0.04, 01 <=...
## $ dst_host_same_srv_rate_disc <fct> 05 > 0.99, 05 > 0.99, 05 > 0....
## $ dst_host_srv_serror_rate_disc <fct> 01 <= 0.95, 01 <= 0.95, 01 <=...
## $ same_srv_rate_disc <fct> 03 > 0.19, 03 > 0.19, 03 > 0....
## $ serror_rate_disc <fct> 01 <= 0.5, 01 <= 0.5, 01 <= 0...
## $ srv_serror_rate_disc <fct> 01 <= 0.51, 01 <= 0.51, 01 <=...
Como la variable fnlwgt ha dado un error con la función smbinning, procederemos a discretizarla manualmente.
4.2.2. Discretización manual
Como las variables src_bytes y duration han dado un error con la función smbinning, procederemos a discretizarlas manualmente.
Variable src_bytes
Vamos a ver la distribución de la variable original, para ver donde podemos hacer los cortes de la discretización:
ggplot(df,aes(src_bytes)) + geom_density()#No se ve muy bien la distribucion por culpa de los atipicos. Vamos a limitar el eje x
ggplot(df,aes(src_bytes)) + geom_density() + scale_x_continuous(limits = c(0, 500000))#Parece que la gran mayoria esta por debajo de 10000, vamos a limitar un poco mas
ggplot(df,aes(src_bytes)) + geom_density() + scale_x_continuous(limits = c(0, 10000))#Parece que la gran mayoria esta por debajo de 1250, vamos a limitar un poco mas
ggplot(df,aes(src_bytes)) + geom_density() + scale_x_continuous(limits = c(0, 1250))#Parece que la gran mayoria esta por debajo de 1100, vamos a limitar un poco mas
ggplot(df,aes(src_bytes)) + geom_density() + scale_x_continuous(limits = c(0, 1100))#Pedimos un histograma para aproximar mejor la forma que queremos conseguir, que es una discretización monotonica
ggplot(df,aes(src_bytes)) + geom_histogram(bins = 15) + scale_x_continuous(limits = c(0, 1100))#Ya sabemos que queremos una forma decreciente ahora veamos como se comporta la variable target para ver si podremos generar algo monotonico, y con la distribucion de la variable target similar en cada tramo. Hay que probar con varios valores para el numero de tramos y coger el que mejor se aproxime.
ggplot(df,aes(src_bytes,fill=intrusion)) + geom_histogram(bins = 15,position='fill') + scale_x_continuous(limits = c(0, 1100)) #Sabiendo ambas cosas vamos a apoyarnos en los deciles para intuir donde podemos hacer buenos cortes y no dejar a muchos clientes en el mismo tramo y asi distribuir los target entre todos los tramos
as.data.frame(quantile(df$src_bytes,prob = seq(0, 1, length = 11)))## quantile(df$src_bytes, prob = seq(0, 1, length = 11))
## 0% 0
## 10% 0
## 20% 0
## 30% 207
## 40% 481
## 50% 520
## 60% 1032
## 70% 1032
## 80% 1032
## 90% 1032
## 100% 693375640
Procedemos a discretizarla manualmente y comprobar la distribución de la variable discretizada es la correcta:
df <- df %>% mutate(src_bytes_disc = as.factor(case_when(
src_bytes <= 50 ~ '01_MENOR_50',
src_bytes > 50 & src_bytes <= 1000 ~ '02_DE_50_A_1000',
src_bytes > 1000 ~ '03_MAS_1000',
TRUE ~ '00_ERROR'))
)
ggplot(df,aes(src_bytes_disc)) + geom_bar()ggplot(df,aes(src_bytes_disc,fill=intrusion)) + geom_bar(position='fill')df <- select(df,-src_bytes)Variable duration
Vamos a ver la distribución de la variable original, para ver donde podemos hacer los cortes de la discretización:
ggplot(df,aes(duration)) + geom_density()#No se ve muy bien la distribucion por culpa de los atipicos. Vamos a limitar el eje x
ggplot(df,aes(duration)) + geom_density() + scale_x_continuous(limits = c(0, 500000))#Parece que la gran mayoria esta por debajo de 10000, vamos a limitar un poco mas
ggplot(df,aes(duration)) + geom_density() + scale_x_continuous(limits = c(0, 10000))#Parece que la gran mayoria esta por debajo de 50, vamos a limitar un poco mas
ggplot(df,aes(duration)) + geom_density() + scale_x_continuous(limits = c(0, 50))#Parece que la gran mayoria esta por debajo de 1100, vamos a limitar un poco mas
ggplot(df,aes(duration)) + geom_density() + scale_x_continuous(limits = c(0, 0.02))Esta variable tiene la mayoría de los valores muy cerca de cero. Si revisamos en la preselección de variables, esta variable era poco significativa en randomForest (puesto 18) y nada significativa con information value (puesto 24 con indice NA). Por lo tanto, con tantos ceros en esta variable, que es la duración de la conexión, y con tan poca significación, la quitamos del dataset.
Procedemos a discretizarla manualmente y comprobar la distribución de la variable discretizada es la correcta:
df <- select(df, -duration)4.2.3. Revisión de la distribución de las variables discretizadas
Vamos a hacer una inspeccion visual de todas las variables a ver si han salido bien. Vemos que todas siguen una distribución coherente con su significado.
df %>%
select_if(is.factor) %>%
gather() %>%
ggplot(aes(value)) +
geom_bar() +
facet_wrap(~ key, scales = "free") + #hace los graficos para todas las variables
theme(axis.text=element_text(size=4))#esto es para cambiar el tamaño del texto del eje y que se lea bienAhora vamos a analizar la penetración de la target en cada categoría para ver si las variables han salido monotónicas
a <- function(var1,var2) {
df_temp <- data.frame(var1 = df[[var1]],var2 = df[[var2]])
df_temp %>%
group_by(var1) %>%
summarise(Conteo = n(), Porc = mean(as.numeric(as.character(var2)))) %>%
ggplot(aes(var1,Porc)) + geom_bar(stat='identity') + xlab(var1)
}
df2_nombres <- df %>% select_if(is.factor) %>% names()
lapply(df2_nombres,function(x){a(x,'intrusion')})## [[1]]
##
## [[2]]
##
## [[3]]
##
## [[4]]
##
## [[5]]
##
## [[6]]
##
## [[7]]
##
## [[8]]
##
## [[9]]
##
## [[10]]
##
## [[11]]
##
## [[12]]
##
## [[13]]
##
## [[14]]
##
## [[15]]
##
## [[16]]
##
## [[17]]
##
## [[18]]
##
## [[19]]
##
## [[20]]
##
## [[21]]
Las distribuciones son correctas, más o menos monotónicas, así que dejaremos el dataset en este punto, con 21 variables independientes que son factores y la variable target.
4.2.4. Organización del dataframe y limpieza del entorno
Antes de continuar vamos a guardar en un objeto de R las discretizaciones, porque las necesitaremos despues para poner el modelo en produccion
#Vamos a crear un objeto de tipo lista que es lo ideal para guardar objetos complejos como las discretizaciones
discretizaciones <- list(
disc_temp_count = disc_temp_count,
disc_temp_diff_srv_rate = disc_temp_diff_srv_rate,
disc_temp_dst_bytes = disc_temp_dst_bytes,
disc_temp_dst_host_count = disc_temp_dst_host_count,
disc_temp_dst_host_diff_srv_rate = disc_temp_dst_host_diff_srv_rate,
disc_temp_dst_host_same_src_port_rate = disc_temp_dst_host_same_src_port_rate,
disc_temp_dst_host_same_srv_rate = disc_temp_dst_host_same_srv_rate,
disc_temp_dst_host_serror_rate = disc_temp_dst_host_serror_rate,
disc_temp_dst_host_srv_count = disc_temp_dst_host_srv_count,
disc_temp_dst_host_srv_diff_host_rate = disc_temp_dst_host_srv_diff_host_rate,
disc_temp_dst_host_srv_serror_rate = disc_temp_dst_host_srv_serror_rate,
disc_temp_same_srv_rate = disc_temp_same_srv_rate,
disc_temp_serror_rate = disc_temp_serror_rate,
disc_temp_srv_count = disc_temp_srv_count,
disc_temp_srv_diff_host_rate = disc_temp_srv_diff_host_rate,
disc_temp_srv_serror_rate = disc_temp_srv_serror_rate
)
saveRDS(discretizaciones,'02_CortesDiscretizaciones.rds')Vamos a ver como ha quedado nuestro fichero antes de pasar a la fase de modelizacion
glimpse(df)## Observations: 494,021
## Variables: 21
## $ logged_in <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ service_disc <fct> 10_user_services, 10_user_ser...
## $ intrusion <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ protocol_disc <fct> 06_tcp_S, 06_tcp_S, 06_tcp_S,...
## $ count_disc <fct> 03 <= 47, 03 <= 47, 03 <= 47,...
## $ dst_bytes_disc <fct> 03 > 2416, 02 <= 2416, 02 <= ...
## $ dst_host_same_src_port_rate_disc <fct> 03 <= 0.99, 03 <= 0.99, 02 <=...
## $ dst_host_srv_diff_host_rate_disc <fct> 01 <= 0, 01 <= 0, 01 <= 0, 01...
## $ srv_diff_host_rate_disc <fct> 01 <= 0, 01 <= 0, 01 <= 0, 01...
## $ dst_host_count_disc <fct> 01 <= 33, 01 <= 33, 01 <= 33,...
## $ srv_count_disc <fct> 03 <= 9, 03 <= 9, 03 <= 9, 03...
## $ diff_srv_rate_disc <fct> 01 <= 0.03, 01 <= 0.03, 01 <=...
## $ dst_host_serror_rate_disc <fct> 01 <= 0.97, 01 <= 0.97, 01 <=...
## $ dst_host_srv_count_disc <fct> 02 <= 9, 04 <= 25, 05 <= 254,...
## $ dst_host_diff_srv_rate_disc <fct> 01 <= 0.04, 01 <= 0.04, 01 <=...
## $ dst_host_same_srv_rate_disc <fct> 05 > 0.99, 05 > 0.99, 05 > 0....
## $ dst_host_srv_serror_rate_disc <fct> 01 <= 0.95, 01 <= 0.95, 01 <=...
## $ same_srv_rate_disc <fct> 03 > 0.19, 03 > 0.19, 03 > 0....
## $ serror_rate_disc <fct> 01 <= 0.5, 01 <= 0.5, 01 <= 0...
## $ srv_serror_rate_disc <fct> 01 <= 0.51, 01 <= 0.51, 01 <=...
## $ src_bytes_disc <fct> 02_DE_50_A_1000, 02_DE_50_A_1...
Vamos a reordernar las variables simplemente por una cuestion estetica:
#creamos un vector con las variables centrales
centrales <- setdiff(names(df),target)
df <- df %>% select(
one_of(centrales),
intrusion)
glimpse(df)## Observations: 494,021
## Variables: 21
## $ logged_in <fct> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ service_disc <fct> 10_user_services, 10_user_ser...
## $ protocol_disc <fct> 06_tcp_S, 06_tcp_S, 06_tcp_S,...
## $ count_disc <fct> 03 <= 47, 03 <= 47, 03 <= 47,...
## $ dst_bytes_disc <fct> 03 > 2416, 02 <= 2416, 02 <= ...
## $ dst_host_same_src_port_rate_disc <fct> 03 <= 0.99, 03 <= 0.99, 02 <=...
## $ dst_host_srv_diff_host_rate_disc <fct> 01 <= 0, 01 <= 0, 01 <= 0, 01...
## $ srv_diff_host_rate_disc <fct> 01 <= 0, 01 <= 0, 01 <= 0, 01...
## $ dst_host_count_disc <fct> 01 <= 33, 01 <= 33, 01 <= 33,...
## $ srv_count_disc <fct> 03 <= 9, 03 <= 9, 03 <= 9, 03...
## $ diff_srv_rate_disc <fct> 01 <= 0.03, 01 <= 0.03, 01 <=...
## $ dst_host_serror_rate_disc <fct> 01 <= 0.97, 01 <= 0.97, 01 <=...
## $ dst_host_srv_count_disc <fct> 02 <= 9, 04 <= 25, 05 <= 254,...
## $ dst_host_diff_srv_rate_disc <fct> 01 <= 0.04, 01 <= 0.04, 01 <=...
## $ dst_host_same_srv_rate_disc <fct> 05 > 0.99, 05 > 0.99, 05 > 0....
## $ dst_host_srv_serror_rate_disc <fct> 01 <= 0.95, 01 <= 0.95, 01 <=...
## $ same_srv_rate_disc <fct> 03 > 0.19, 03 > 0.19, 03 > 0....
## $ serror_rate_disc <fct> 01 <= 0.5, 01 <= 0.5, 01 <= 0...
## $ srv_serror_rate_disc <fct> 01 <= 0.51, 01 <= 0.51, 01 <=...
## $ src_bytes_disc <fct> 02_DE_50_A_1000, 02_DE_50_A_1...
## $ intrusion <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
Limpiamos el entorno de cualquier cosa que no sea el dataframe. Primero lo salvamos en un fichero.
saveRDS(df,'cache3_dataset_sinteticas.rds')Limpiamos la memoria de todas las variables.
a_borrar <- setdiff(ls(),c("df","a_factores","discretizar","contar_ceros"))
rm(list=c(a_borrar,'a_borrar'))Cargamos el cache temporal
df <- readRDS('cache3_dataset_sinteticas.rds')5. Modelizacion
5.1 - Preparar las funciones que vamos a necesitar
Funcion para crear una matriz de confusion.
confusion<-function(real,scoring,umbral){
conf<-table(real,scoring>=umbral)
if(ncol(conf)==2) return(conf) else return(NULL)
}Funcion para calcular las metricas de los modelos: acierto, precision, cobertura y F1
metricas<-function(matriz_conf){
acierto <- (matriz_conf[1,1] + matriz_conf[2,2]) / sum(matriz_conf) *100
precision <- matriz_conf[2,2] / (matriz_conf[2,2] + matriz_conf[1,2]) *100
cobertura <- matriz_conf[2,2] / (matriz_conf[2,2] + matriz_conf[2,1]) *100
F1 <- 2*precision*cobertura/(precision+cobertura)
salida<-c(acierto,precision,cobertura,F1)
return(salida)
}Funcion para probar distintos umbrales y ver el efecto sobre precision y cobertura. Genera una tabla con las diferentes metricas para cada umbral de corte, variando un 5% este umbral.
umbrales<-function(real,scoring){
umbrales<-data.frame(umbral=rep(0,times=19),acierto=rep(0,times=19),precision=rep(0,times=19),cobertura=rep(0,times=19),F1=rep(0,times=19))
cont <- 1
for (cada in seq(0.05,0.95,by = 0.05)){
datos<-metricas(confusion(real,scoring,cada))
registro<-c(cada,datos)
umbrales[cont,]<-registro
cont <- cont + 1
}
return(umbrales)
}Funciones que calculan la curva ROC y el AUC
roc<-function(prediction){
r<-performance(prediction,'tpr','fpr')
plot(r)
}
auc<-function(prediction){
a<-performance(prediction,'auc')
return(a@y.values[[1]])
}5.2 - Creamos las particiones de training (70%) y test (30%)
Establecemos una semilla para que nos salgan los mismos resultados en todas las ejecuciones:
set.seed(12345)Generamos una variable aleatoria con una distribucion 70-30
df$random<-sample(0:1,size = nrow(df),replace = T,prob = c(0.3,0.7))
train<-filter(df,random==1)
test<-filter(df,random==0)
#Eliminamos ya la random para que no moleste
df$random <- NULLIdentificamos las variables independientes y la target:
#Las independientes seran todas menos la target
independientes <- setdiff(names(df),c('intrusion'))
target <- 'intrusion'
independientes## [1] "logged_in" "service_disc"
## [3] "protocol_disc" "count_disc"
## [5] "dst_bytes_disc" "dst_host_same_src_port_rate_disc"
## [7] "dst_host_srv_diff_host_rate_disc" "srv_diff_host_rate_disc"
## [9] "dst_host_count_disc" "srv_count_disc"
## [11] "diff_srv_rate_disc" "dst_host_serror_rate_disc"
## [13] "dst_host_srv_count_disc" "dst_host_diff_srv_rate_disc"
## [15] "dst_host_same_srv_rate_disc" "dst_host_srv_serror_rate_disc"
## [17] "same_srv_rate_disc" "serror_rate_disc"
## [19] "srv_serror_rate_disc" "src_bytes_disc"
target## [1] "intrusion"
Creamos la formula para usar en los modelos
formula <- reformulate(independientes,target)
formula## intrusion ~ logged_in + service_disc + protocol_disc + count_disc +
## dst_bytes_disc + dst_host_same_src_port_rate_disc + dst_host_srv_diff_host_rate_disc +
## srv_diff_host_rate_disc + dst_host_count_disc + srv_count_disc +
## diff_srv_rate_disc + dst_host_serror_rate_disc + dst_host_srv_count_disc +
## dst_host_diff_srv_rate_disc + dst_host_same_srv_rate_disc +
## dst_host_srv_serror_rate_disc + same_srv_rate_disc + serror_rate_disc +
## srv_serror_rate_disc + src_bytes_disc
5.3 - Modelizamos con regresion logistica
Primero vamos a hacer un modelo con todas las variables. Cargamos la formula en una copia para la regresion logistica, ya que iremos haciendo cambios específicos para la regresión logistica. La preselección de variables es básica para la regresión logística, ya que funciona mejor con pocas variables y que no tengan correlación entre ellas.
formula_rl <- formula
rl <- glm(formula_rl,train,family=binomial(link='logit'))
summary(rl)##
## Call:
## glm(formula = formula_rl, family = binomial(link = "logit"),
## data = train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -5.693 0.000 0.000 0.000 4.874
##
## Coefficients:
## Estimate Std. Error z value
## (Intercept) 21.12110 79461.22471 0.000
## logged_in1 0.62542 0.19544 3.200
## service_disc02_csnet_ns -2.43849 79671.60003 0.000
## service_disc03_ctf -5.04426 79717.60289 0.000
## service_disc04_discard -9.96027 79644.56894 0.000
## service_disc05_time -20.56645 79461.22471 0.000
## service_disc06_network -19.61622 79461.22471 0.000
## service_disc07_remote_exec -17.05513 79461.22471 0.000
## service_disc08_other -22.92513 79461.22471 0.000
## service_disc09_domain -22.61231 79461.22471 0.000
## service_disc10_user_services -21.13506 79461.22471 0.000
## service_disc11_private -11.57996 79461.22471 0.000
## service_disc12_isp 3.16367 79495.72590 0.000
## protocol_disc02_icmp_SF -21.28651 2341.83801 -0.009
## protocol_disc03_tcp_OTH 5.01926 11.77754 0.426
## protocol_disc04_tcp_REJ -1.39275 0.38200 -3.646
## protocol_disc05_tcp_RS 7.31057 0.46475 15.730
## protocol_disc06_tcp_S 3.82526 0.41969 9.115
## count_disc02 <= 5 0.93593 0.10983 8.522
## count_disc03 <= 47 -1.08481 0.19231 -5.641
## count_disc04 <= 123 2.93909 0.31984 9.189
## count_disc05 <= 200 3.92799 0.61550 6.382
## count_disc06 <= 252 5.68810 0.70517 8.066
## count_disc07 <= 431 5.53219 0.66425 8.329
## count_disc08 <= 509 10.63312 2.47291 4.300
## count_disc09 > 509 10.16870 1.22007 8.335
## dst_bytes_disc02 <= 2416 -4.66751 0.17355 -26.894
## dst_bytes_disc03 > 2416 0.31790 0.14994 2.120
## dst_host_same_src_port_rate_disc02 <= 0.04 -1.27518 0.13786 -9.250
## dst_host_same_src_port_rate_disc03 <= 0.99 -2.53816 0.16755 -15.149
## dst_host_same_src_port_rate_disc04 > 0.99 2.23349 0.19807 11.276
## dst_host_srv_diff_host_rate_disc02 > 0 0.79000 0.13290 5.944
## srv_diff_host_rate_disc02 > 0 -0.71590 0.12127 -5.903
## dst_host_count_disc02 <= 254 -2.05565 0.13806 -14.889
## dst_host_count_disc03 > 254 -0.40611 0.19468 -2.086
## srv_count_disc02 <= 5 0.55843 0.12313 4.535
## srv_count_disc03 <= 9 1.41314 0.19181 7.368
## srv_count_disc04 <= 14 1.66363 0.21138 7.870
## srv_count_disc05 <= 19 2.11732 0.22496 9.412
## srv_count_disc06 <= 325 3.00247 0.19325 15.537
## srv_count_disc07 <= 509 3.21325 2.45898 1.307
## srv_count_disc08 <= 510 0.08006 1.58913 0.050
## srv_count_disc09 > 510 16.17363 160.61688 0.101
## diff_srv_rate_disc02 <= 0.06 1.89712 0.71708 2.646
## diff_srv_rate_disc03 > 0.06 0.35212 0.22990 1.532
## dst_host_serror_rate_disc02 > 0.97 2.42659 0.70618 3.436
## dst_host_srv_count_disc02 <= 9 -1.17526 0.24146 -4.867
## dst_host_srv_count_disc03 <= 16 -1.52226 0.26675 -5.707
## dst_host_srv_count_disc04 <= 25 -1.41594 0.27571 -5.136
## dst_host_srv_count_disc05 <= 254 -2.07103 0.25031 -8.274
## dst_host_srv_count_disc06 > 254 -4.01236 0.28161 -14.248
## dst_host_diff_srv_rate_disc02 <= 0.06 0.38446 0.19005 2.023
## dst_host_diff_srv_rate_disc03 <= 0.0701 0.47564 0.31572 1.507
## dst_host_diff_srv_rate_disc04 > 0.0701 0.40202 0.13913 2.889
## dst_host_same_srv_rate_disc02 <= 0.06 0.31074 0.31531 0.986
## dst_host_same_srv_rate_disc03 <= 0.1 -0.34314 0.34943 -0.982
## dst_host_same_srv_rate_disc04 <= 0.99 -1.93635 0.29738 -6.511
## dst_host_same_srv_rate_disc05 > 0.99 1.29962 0.32071 4.052
## dst_host_srv_serror_rate_disc02 > 0.95 2.78426 0.79062 3.522
## same_srv_rate_disc02 <= 0.19 0.63206 0.54246 1.165
## same_srv_rate_disc03 > 0.19 -3.36019 0.46003 -7.304
## serror_rate_disc02 > 0.5 -3.21096 0.96998 -3.310
## srv_serror_rate_disc02 > 0.51 5.99667 0.89020 6.736
## src_bytes_disc02_DE_50_A_1000 -3.87560 0.15357 -25.237
## src_bytes_disc03_MAS_1000 3.27918 0.18567 17.662
## Pr(>|z|)
## (Intercept) 0.999788
## logged_in1 0.001374 **
## service_disc02_csnet_ns 0.999976
## service_disc03_ctf 0.999950
## service_disc04_discard 0.999900
## service_disc05_time 0.999793
## service_disc06_network 0.999803
## service_disc07_remote_exec 0.999829
## service_disc08_other 0.999770
## service_disc09_domain 0.999773
## service_disc10_user_services 0.999788
## service_disc11_private 0.999884
## service_disc12_isp 0.999968
## protocol_disc02_icmp_SF 0.992748
## protocol_disc03_tcp_OTH 0.669982
## protocol_disc04_tcp_REJ 0.000266 ***
## protocol_disc05_tcp_RS < 0.0000000000000002 ***
## protocol_disc06_tcp_S < 0.0000000000000002 ***
## count_disc02 <= 5 < 0.0000000000000002 ***
## count_disc03 <= 47 0.000000016917818225 ***
## count_disc04 <= 123 < 0.0000000000000002 ***
## count_disc05 <= 200 0.000000000175080243 ***
## count_disc06 <= 252 0.000000000000000725 ***
## count_disc07 <= 431 < 0.0000000000000002 ***
## count_disc08 <= 509 0.000017092282429461 ***
## count_disc09 > 509 < 0.0000000000000002 ***
## dst_bytes_disc02 <= 2416 < 0.0000000000000002 ***
## dst_bytes_disc03 > 2416 0.033989 *
## dst_host_same_src_port_rate_disc02 <= 0.04 < 0.0000000000000002 ***
## dst_host_same_src_port_rate_disc03 <= 0.99 < 0.0000000000000002 ***
## dst_host_same_src_port_rate_disc04 > 0.99 < 0.0000000000000002 ***
## dst_host_srv_diff_host_rate_disc02 > 0 0.000000002778605397 ***
## srv_diff_host_rate_disc02 > 0 0.000000003561094734 ***
## dst_host_count_disc02 <= 254 < 0.0000000000000002 ***
## dst_host_count_disc03 > 254 0.036978 *
## srv_count_disc02 <= 5 0.000005754843106925 ***
## srv_count_disc03 <= 9 0.000000000000173796 ***
## srv_count_disc04 <= 14 0.000000000000003535 ***
## srv_count_disc05 <= 19 < 0.0000000000000002 ***
## srv_count_disc06 <= 325 < 0.0000000000000002 ***
## srv_count_disc07 <= 509 0.191300
## srv_count_disc08 <= 510 0.959822
## srv_count_disc09 > 510 0.919791
## diff_srv_rate_disc02 <= 0.06 0.008154 **
## diff_srv_rate_disc03 > 0.06 0.125611
## dst_host_serror_rate_disc02 > 0.97 0.000590 ***
## dst_host_srv_count_disc02 <= 9 0.000001131738251951 ***
## dst_host_srv_count_disc03 <= 16 0.000000011513018100 ***
## dst_host_srv_count_disc04 <= 25 0.000000281215978941 ***
## dst_host_srv_count_disc05 <= 254 < 0.0000000000000002 ***
## dst_host_srv_count_disc06 > 254 < 0.0000000000000002 ***
## dst_host_diff_srv_rate_disc02 <= 0.06 0.043082 *
## dst_host_diff_srv_rate_disc03 <= 0.0701 0.131934
## dst_host_diff_srv_rate_disc04 > 0.0701 0.003860 **
## dst_host_same_srv_rate_disc02 <= 0.06 0.324377
## dst_host_same_srv_rate_disc03 <= 0.1 0.326098
## dst_host_same_srv_rate_disc04 <= 0.99 0.000000000074519028 ***
## dst_host_same_srv_rate_disc05 > 0.99 0.000050719855128111 ***
## dst_host_srv_serror_rate_disc02 > 0.95 0.000429 ***
## same_srv_rate_disc02 <= 0.19 0.243946
## same_srv_rate_disc03 > 0.19 0.000000000000278825 ***
## serror_rate_disc02 > 0.5 0.000932 ***
## srv_serror_rate_disc02 > 0.51 0.000000000016249460 ***
## src_bytes_disc02_DE_50_A_1000 < 0.0000000000000002 ***
## src_bytes_disc03_MAS_1000 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 343208.4 on 345606 degrees of freedom
## Residual deviance: 7204.9 on 345542 degrees of freedom
## AIC: 7334.9
##
## Number of Fisher Scoring iterations: 22
Análisis de la salida:
- La distribución de los residuos parece que sigue una distribucion más o menos normal, lo que es correcto.
- Coeficientes: todos tienen sentido en los signos como en los valores.
Vamos a seleccionar aquellas variables que tiene el valor máximo de significación en algunos de sus tramos, para volver a generar el modelo solo con estas variables más significativas.
a_mantener <- c(
'protocol_disc',
'count_disc',
'dst_bytes_disc',
'dst_host_same_src_port_rate_disc',
'dst_host_srv_diff_host_rate_disc',
'srv_diff_host_rate_disc',
'dst_host_count_disc',
'srv_count_disc',
'dst_host_serror_rate_disc',
'dst_host_srv_count_disc',
'dst_host_same_srv_rate_disc',
'dst_host_srv_serror_rate_disc',
'same_srv_rate_disc',
'serror_rate_disc',
'srv_serror_rate_disc',
'src_bytes_disc'
)
formula_rl <- reformulate(a_mantener,target)
rl<- glm(formula_rl,train,family=binomial(link='logit'))
summary(rl)##
## Call:
## glm(formula = formula_rl, family = binomial(link = "logit"),
## data = train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -4.8263 0.0000 0.0000 0.0006 4.8631
##
## Coefficients:
## Estimate Std. Error z value
## (Intercept) 7.54621 0.35702 21.137
## protocol_disc02_icmp_SF -0.66700 0.12259 -5.441
## protocol_disc03_tcp_OTH 4.25644 1.20338 3.537
## protocol_disc04_tcp_REJ -2.72867 0.13429 -20.320
## protocol_disc05_tcp_RS 3.57443 0.26575 13.450
## protocol_disc06_tcp_S 0.24845 0.13485 1.842
## count_disc02 <= 5 0.80801 0.08343 9.685
## count_disc03 <= 47 -1.65012 0.14961 -11.030
## count_disc04 <= 123 1.42867 0.20525 6.961
## count_disc05 <= 200 0.91243 0.30616 2.980
## count_disc06 <= 252 1.41117 0.36814 3.833
## count_disc07 <= 431 0.89653 0.40764 2.199
## count_disc08 <= 509 6.23721 1.45873 4.276
## count_disc09 > 509 7.94279 1.62171 4.898
## dst_bytes_disc02 <= 2416 -6.50202 0.17194 -37.817
## dst_bytes_disc03 > 2416 -0.03110 0.13213 -0.235
## dst_host_same_src_port_rate_disc02 <= 0.04 -0.64161 0.11993 -5.350
## dst_host_same_src_port_rate_disc03 <= 0.99 -2.32344 0.13585 -17.103
## dst_host_same_src_port_rate_disc04 > 0.99 0.88121 0.15108 5.833
## dst_host_srv_diff_host_rate_disc02 > 0 -0.11051 0.10774 -1.026
## srv_diff_host_rate_disc02 > 0 -1.26756 0.09074 -13.969
## dst_host_count_disc02 <= 254 -2.30910 0.11206 -20.607
## dst_host_count_disc03 > 254 -0.47828 0.15488 -3.088
## srv_count_disc02 <= 5 0.63612 0.09587 6.635
## srv_count_disc03 <= 9 1.56807 0.15250 10.282
## srv_count_disc04 <= 14 1.68919 0.17081 9.889
## srv_count_disc05 <= 19 2.49830 0.17803 14.033
## srv_count_disc06 <= 325 3.55190 0.15534 22.865
## srv_count_disc07 <= 509 5.92569 1.41760 4.180
## srv_count_disc08 <= 510 1.62371 1.61120 1.008
## srv_count_disc09 > 510 17.80711 165.22057 0.108
## dst_host_serror_rate_disc02 > 0.97 0.81956 0.57282 1.431
## dst_host_srv_count_disc02 <= 9 -0.71890 0.17840 -4.030
## dst_host_srv_count_disc03 <= 16 -1.01343 0.20730 -4.889
## dst_host_srv_count_disc04 <= 25 -0.99188 0.21791 -4.552
## dst_host_srv_count_disc05 <= 254 -1.82224 0.19592 -9.301
## dst_host_srv_count_disc06 > 254 -3.39268 0.21973 -15.440
## dst_host_same_srv_rate_disc02 <= 0.06 1.51567 0.22002 6.889
## dst_host_same_srv_rate_disc03 <= 0.1 1.02176 0.25525 4.003
## dst_host_same_srv_rate_disc04 <= 0.99 -0.33245 0.21891 -1.519
## dst_host_same_srv_rate_disc05 > 0.99 0.80678 0.22542 3.579
## dst_host_srv_serror_rate_disc02 > 0.95 2.42633 0.69024 3.515
## same_srv_rate_disc02 <= 0.19 -1.55743 0.38213 -4.076
## same_srv_rate_disc03 > 0.19 -5.25708 0.31391 -16.747
## serror_rate_disc02 > 0.5 0.84335 0.55439 1.521
## srv_serror_rate_disc02 > 0.51 1.80556 0.66585 2.712
## src_bytes_disc02_DE_50_A_1000 -3.89414 0.11513 -33.825
## src_bytes_disc03_MAS_1000 1.55717 0.12333 12.626
## Pr(>|z|)
## (Intercept) < 0.0000000000000002 ***
## protocol_disc02_icmp_SF 0.00000005300331 ***
## protocol_disc03_tcp_OTH 0.000405 ***
## protocol_disc04_tcp_REJ < 0.0000000000000002 ***
## protocol_disc05_tcp_RS < 0.0000000000000002 ***
## protocol_disc06_tcp_S 0.065417 .
## count_disc02 <= 5 < 0.0000000000000002 ***
## count_disc03 <= 47 < 0.0000000000000002 ***
## count_disc04 <= 123 0.00000000000339 ***
## count_disc05 <= 200 0.002880 **
## count_disc06 <= 252 0.000126 ***
## count_disc07 <= 431 0.027856 *
## count_disc08 <= 509 0.00001904753177 ***
## count_disc09 > 509 0.00000096921048 ***
## dst_bytes_disc02 <= 2416 < 0.0000000000000002 ***
## dst_bytes_disc03 > 2416 0.813939
## dst_host_same_src_port_rate_disc02 <= 0.04 0.00000008803478 ***
## dst_host_same_src_port_rate_disc03 <= 0.99 < 0.0000000000000002 ***
## dst_host_same_src_port_rate_disc04 > 0.99 0.00000000545325 ***
## dst_host_srv_diff_host_rate_disc02 > 0 0.305033
## srv_diff_host_rate_disc02 > 0 < 0.0000000000000002 ***
## dst_host_count_disc02 <= 254 < 0.0000000000000002 ***
## dst_host_count_disc03 > 254 0.002015 **
## srv_count_disc02 <= 5 0.00000000003234 ***
## srv_count_disc03 <= 9 < 0.0000000000000002 ***
## srv_count_disc04 <= 14 < 0.0000000000000002 ***
## srv_count_disc05 <= 19 < 0.0000000000000002 ***
## srv_count_disc06 <= 325 < 0.0000000000000002 ***
## srv_count_disc07 <= 509 0.00002914029799 ***
## srv_count_disc08 <= 510 0.313567
## srv_count_disc09 > 510 0.914172
## dst_host_serror_rate_disc02 > 0.97 0.152506
## dst_host_srv_count_disc02 <= 9 0.00005587469013 ***
## dst_host_srv_count_disc03 <= 16 0.00000101501590 ***
## dst_host_srv_count_disc04 <= 25 0.00000532177719 ***
## dst_host_srv_count_disc05 <= 254 < 0.0000000000000002 ***
## dst_host_srv_count_disc06 > 254 < 0.0000000000000002 ***
## dst_host_same_srv_rate_disc02 <= 0.06 0.00000000000562 ***
## dst_host_same_srv_rate_disc03 <= 0.1 0.00006253422686 ***
## dst_host_same_srv_rate_disc04 <= 0.99 0.128856
## dst_host_same_srv_rate_disc05 > 0.99 0.000345 ***
## dst_host_srv_serror_rate_disc02 > 0.95 0.000439 ***
## same_srv_rate_disc02 <= 0.19 0.00004587685946 ***
## same_srv_rate_disc03 > 0.19 < 0.0000000000000002 ***
## serror_rate_disc02 > 0.5 0.128201
## srv_serror_rate_disc02 > 0.51 0.006694 **
## src_bytes_disc02_DE_50_A_1000 < 0.0000000000000002 ***
## src_bytes_disc03_MAS_1000 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 343208 on 345606 degrees of freedom
## Residual deviance: 10636 on 345559 degrees of freedom
## AIC: 10732
##
## Number of Fisher Scoring iterations: 22
Ahora todas las variables son significativas, y los coeficientes siguen siendo coherentes con el significado de las variables.
Y calculamos el pseudo R cuadrado, a partir de 0.5 el modelo puede considerarse bueno, y nos clasifica bien el trafico en normal o malicioso:
pr2_rl <- 1 -(rl$deviance / rl$null.deviance)
pr2_rl## [1] 0.9690094
Aplicamos el modelo al conjunto de test, generando un vector con las probabilidades
rl_predict <- predict(rl,test,type = 'response')Vemos que pinta tiene mirando la variable target con la predicción:
plot(rl_predict~test$intrusion)Aunque las variables están separadas y el modelo predice los 0/1, hay bastante separación entre las medias de 0 y 1, con lo que el modelo los distingue bien.
Ahora tenemos que transformar la probabilidad en una decision del tipo de trafico en una decisión de trafico normal o malicioso.
Con la funcion umbrales probamos diferentes cortes
umb_rl<-umbrales(test$intrusion,rl_predict)
umb_rl## umbral acierto precision cobertura F1
## 1 0.05 98.50351 98.22945 99.93965 99.07717
## 2 0.10 99.05669 98.93333 99.90360 99.41610
## 3 0.15 99.24805 99.18348 99.88684 99.53392
## 4 0.20 99.27702 99.24934 99.85582 99.55166
## 5 0.25 99.40100 99.41491 99.84241 99.62820
## 6 0.30 99.44749 99.47881 99.83571 99.65694
## 7 0.35 99.51554 99.66327 99.73428 99.69876
## 8 0.40 99.52969 99.69079 99.72422 99.70750
## 9 0.45 99.56001 99.76845 99.68398 99.72620
## 10 0.50 99.56675 99.80106 99.65968 99.73032
## 11 0.55 99.53306 99.81436 99.60435 99.70925
## 12 0.60 99.51554 99.83861 99.55825 99.69823
## 13 0.65 99.50881 99.85871 99.52975 99.69396
## 14 0.70 99.48455 99.86538 99.49287 99.67878
## 15 0.75 99.42930 99.87873 99.41072 99.64417
## 16 0.80 99.33632 99.89710 99.27660 99.58588
## 17 0.85 99.23996 99.92311 99.13075 99.52535
## 18 0.90 99.06006 99.92886 98.90107 99.41231
## 19 0.95 98.80267 99.96599 98.54398 99.24989
Seleccionamos el umbral que maximiza la F1
umbral_final_rl<-umb_rl[which.max(umb_rl$F1),1]
umbral_final_rl## [1] 0.5
Evaluamos la matriz de confusion y las metricas con el umbral optimizado
confusion(test$intrusion,rl_predict,umbral_final_rl)##
## real FALSE TRUE
## 0 28879 237
## 1 406 118892
rl_metricas<-filter(umb_rl,umbral==umbral_final_rl)
rl_metricas## umbral acierto precision cobertura F1
## 1 0.5 99.56675 99.80106 99.65968 99.73032
Evaluamos la ROC
#creamos el objeto prediction
rl_prediction<-prediction(rl_predict,test$intrusion)
#visualizamos la ROC
roc(rl_prediction)Sacamos las metricas definitivas incluyendo el AUC, que es un 69%.
rl_metricas<-cbind(rl_metricas,AUC=round(auc(rl_prediction),2)*100)
print(t(rl_metricas))## [,1]
## umbral 0.50000
## acierto 99.56675
## precision 99.80106
## cobertura 99.65968
## F1 99.73032
## AUC 100.00000
También dibujamos el gain chart:
plot(performance(rl_prediction,'tpr','rpp'))5.4 - Modelizamos con Arboles de decision
5.4.1. Modelización
Los arboles de decisión permiten explicar muy bien las decisiones que toman sobre los datos.
Creamos el primer modelo
formula_ar <- formula
ar <- rpart(formula_ar, train, method = 'class', parms = list(
split = "information"),
control = rpart.control(cp = 0.00001))Revisamos donde el error de validacion cruzada empieza a crecer. Hay que fijarse en el xerror, donde cambia de decreciente a creciente.
printcp(ar)##
## Classification tree:
## rpart(formula = formula_ar, data = train, method = "class", parms = list(split = "information"),
## control = rpart.control(cp = 0.00001))
##
## Variables actually used in tree construction:
## [1] count_disc diff_srv_rate_disc
## [3] dst_bytes_disc dst_host_count_disc
## [5] dst_host_diff_srv_rate_disc dst_host_same_src_port_rate_disc
## [7] dst_host_same_srv_rate_disc dst_host_srv_count_disc
## [9] dst_host_srv_diff_host_rate_disc logged_in
## [11] protocol_disc service_disc
## [13] src_bytes_disc srv_count_disc
## [15] srv_serror_rate_disc
##
## Root node error: 68162/345607 = 0.19722
##
## n= 345607
##
## CP nsplit rel error xerror xstd
## 1 0.896540594 0 1.0000000 1.0000000 0.00343183
## 2 0.021463572 1 0.1034594 0.1034594 0.00121937
## 3 0.014201461 4 0.0361492 0.0361638 0.00072579
## 4 0.002508729 5 0.0219477 0.0219624 0.00056640
## 5 0.002428039 6 0.0194390 0.0197764 0.00053759
## 6 0.002132175 8 0.0145829 0.0146269 0.00046257
## 7 0.000616179 11 0.0081864 0.0081277 0.00034504
## 8 0.000381444 12 0.0075702 0.0075409 0.00033237
## 9 0.000337431 13 0.0071888 0.0073941 0.00032912
## 10 0.000293419 14 0.0068513 0.0071007 0.00032253
## 11 0.000249406 16 0.0062645 0.0064405 0.00030719
## 12 0.000185832 17 0.0060151 0.0062351 0.00030226
## 13 0.000161380 21 0.0052229 0.0054576 0.00028281
## 14 0.000146709 23 0.0049001 0.0052375 0.00027706
## 15 0.000096828 25 0.0046067 0.0049294 0.00026879
## 16 0.000088026 30 0.0041225 0.0046507 0.00026109
## 17 0.000073355 32 0.0039465 0.0042692 0.00025016
## 18 0.000058684 37 0.0035797 0.0041372 0.00024627
## 19 0.000029342 39 0.0034623 0.0038144 0.00023647
## 20 0.000014671 45 0.0032863 0.0037558 0.00023465
## 21 0.000011737 47 0.0032569 0.0037264 0.00023373
## 22 0.000010000 52 0.0031983 0.0037264 0.00023373
plotcp(ar)No parece que se minimiza. Según el grafico, sobre un parametro de complejidad de 0.0003, se produce una ligera subida, y a partir de ahí más o menos se mantiene. Generamos un nuevo arbol con ese parametro. Ademas vamos a incluir un nuevo paramtero para que el arbol no tenga mas de 10 niveles.
ar <- rpart(formula_ar, train, method = 'class', parms = list(
split = "information"),
control = rpart.control(cp = 0.0003,maxdepth = 10))Revisamos de nuevo la complejidad.
printcp(ar)##
## Classification tree:
## rpart(formula = formula_ar, data = train, method = "class", parms = list(split = "information"),
## control = rpart.control(cp = 0.0003, maxdepth = 10))
##
## Variables actually used in tree construction:
## [1] count_disc dst_bytes_disc
## [3] dst_host_count_disc dst_host_diff_srv_rate_disc
## [5] dst_host_same_srv_rate_disc dst_host_srv_diff_host_rate_disc
## [7] service_disc src_bytes_disc
## [9] srv_count_disc srv_serror_rate_disc
##
## Root node error: 68162/345607 = 0.19722
##
## n= 345607
##
## CP nsplit rel error xerror xstd
## 1 0.89654059 0 1.0000000 1.0000000 0.00343183
## 2 0.02146357 1 0.1034594 0.1034594 0.00121937
## 3 0.01420146 4 0.0361492 0.0361638 0.00072579
## 4 0.00250873 5 0.0219477 0.0219624 0.00056640
## 5 0.00242804 6 0.0194390 0.0190722 0.00052797
## 6 0.00213218 8 0.0145829 0.0146122 0.00046234
## 7 0.00061618 11 0.0081864 0.0080690 0.00034379
## 8 0.00038144 12 0.0075702 0.0074382 0.00033010
## 9 0.00033743 13 0.0071888 0.0073061 0.00032716
## 10 0.00030000 14 0.0068513 0.0069540 0.00031919
plotcp(ar)Ahora parece bastante estable, ya que el error es solo decreciente y no vuelve a crecer. Habría que variar el parametro cp para ver que luego volvería a crecer el error y comprobar que este es el mejor corte.
5.4.2. Visualización del arbol
Vamos a crear el grafico del arbol para analizarlo.
rpart.plot(ar,type=2,extra = 7, under = TRUE,under.cex = 0.7,fallen.leaves=F,gap = 0,cex=0.2,yesno = 2,box.palette = "GnYlRd",branch.lty = 3)Otra visualizacion alternativa
prp(ar, type=2, extra=104, nn=TRUE, fallen.leaves=F,
faclen=0, varlen=0, shadow.col="grey", branch.lty=3)5.4.3. Extracción de las reglas del modelo
Vamos a sacar las reglas que podrian ser utilizadas por ejemplo para hacer una implantacion del arbol. Normalmente los data scientist generan el modelo y los insights con un lenguaje analitico como R, y luego un equipo de data engineers generan esas reglas en C++, Java o SQL para que se ejecuten sobre una base de datos, donde tendremos los datos de origen.
reglas <- rpart.rules(ar,style = 'tall',cover = T)
#style sirve para que la salida sea mas legible y cover añade el % de casos e los que aplica la reglaPodemos llevarnos el nodo final de cada cliente a un data.frame para poder hacer una explotacion posterior.
#Para ello usarmos el predict especficio de rpart y con el parametro nn
ar_numnodos <- rpart.predict(ar,test,nn = T)
head(ar_numnodos)## 0 1 nn
## 1 0.998561 0.001438966 8
## 2 0.998561 0.001438966 8
## 3 0.998561 0.001438966 8
## 4 0.998561 0.001438966 8
## 5 0.998561 0.001438966 8
## 6 0.998561 0.001438966 8
5.4.4. Evaluación del modelo
Vamos a calcular los scorings y evaluar el modelo
ar_predict <- predict(ar,test,type = 'prob')[,2]Vemos que pinta tiene
plot(ar_predict~test$intrusion)Con la funcion umbrales probamos diferentes cortes
umb_ar<-umbrales(test$intrusion,ar_predict)
umb_ar## umbral acierto precision cobertura F1
## 1 0.05 99.87265 99.94214 99.89941 99.92077
## 2 0.10 99.87265 99.94214 99.89941 99.92077
## 3 0.15 99.87265 99.94214 99.89941 99.92077
## 4 0.20 99.87265 99.94214 99.89941 99.92077
## 5 0.25 99.87265 99.94214 99.89941 99.92077
## 6 0.30 99.87265 99.94214 99.89941 99.92077
## 7 0.35 99.87265 99.94214 99.89941 99.92077
## 8 0.40 99.87265 99.94214 99.89941 99.92077
## 9 0.45 99.87265 99.94214 99.89941 99.92077
## 10 0.50 99.87265 99.94214 99.89941 99.92077
## 11 0.55 99.87265 99.94214 99.89941 99.92077
## 12 0.60 99.87265 99.94214 99.89941 99.92077
## 13 0.65 99.87265 99.94214 99.89941 99.92077
## 14 0.70 99.87265 99.94214 99.89941 99.92077
## 15 0.75 99.86389 99.94632 99.88432 99.91531
## 16 0.80 99.86389 99.94632 99.88432 99.91531
## 17 0.85 99.86389 99.94632 99.88432 99.91531
## 18 0.90 99.86389 99.94632 99.88432 99.91531
## 19 0.95 99.72442 99.95714 99.69991 99.82836
Seleccionamos automaticamente el mejor umbral
umbral_final_ar<-umb_ar[which.max(umb_ar$F1),1]
umbral_final_ar## [1] 0.05
Evaluamos la matriz de confusion y las metricas con el umbral optimizado
confusion(test$intrusion,ar_predict,umbral_final_ar)##
## real FALSE TRUE
## 0 29047 69
## 1 120 119178
ar_metricas<-filter(umb_ar,umbral==umbral_final_ar)
ar_metricas## umbral acierto precision cobertura F1
## 1 0.05 99.87265 99.94214 99.89941 99.92077
Evaluamos la ROC
#creamos el objeto prediction
ar_prediction<-prediction(ar_predict,test$intrusion)
#visualizamos la ROC
roc(ar_prediction)Sacamos las metricas definitivas incluyendo el AUC
ar_metricas<-cbind(ar_metricas,AUC=round(auc(ar_prediction),2)*100)
print(t(ar_metricas))## [,1]
## umbral 0.05000
## acierto 99.87265
## precision 99.94214
## cobertura 99.89941
## F1 99.92077
## AUC 100.00000