Predicción de ciberataques analizando el trafico de red


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 service

3 - 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 dataframe

Decision: 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 bien

Ahora 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 <- NULL

Identificamos 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 regla

Podemos 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

Andres Lopez

6 de diciembre de 2019