Twoja kolej!

Teraz nadszedł czas na przetestowanie tych metod (regresja grzbietowa i lasso) oraz metod oceny (zestaw walidacyjny, walidacja krzyżowa) na innych zbiorach danych. Możesz pracować z zespołem nad tą częścią laboratorium.

Możesz użyć dowolnego zbioru danych zawartego w ISLR lub wybrać jeden z pakietów danych na Kaggle/Data World itp. (zmienna zależna musi być ciągła).

Pobierz zbiór danych i spróbuj określić optymalny zestaw parametrów, które należy użyć do jego modelowania!

Credit=na.omit(Credit)
x = model.matrix(Balance~., Credit)[,-1] # przycinam pierwszą kolumnę
                                         # zostawiam predyktory
y = Credit %>%
  select(Balance) %>%
  unlist() %>%
  as.numeric()

modellm <- lm( y~x)
summary(modellm)
## 
## Call:
## lm(formula = y ~ x)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -166.48  -77.62  -14.37   56.21  316.52 
## 
## Coefficients:
##                       Estimate Std. Error t value Pr(>|t|)    
## (Intercept)         -487.07424   36.73407 -13.259  < 2e-16 ***
## xID                    0.04105    0.04343   0.945   0.3452    
## xIncome               -7.80740    0.23431 -33.321  < 2e-16 ***
## xLimit                 0.19052    0.03279   5.811  1.3e-08 ***
## xRating                1.14249    0.49100   2.327   0.0205 *  
## xCards                17.83639    4.34324   4.107  4.9e-05 ***
## xAge                  -0.62955    0.29449  -2.138   0.0332 *  
## xEducation            -1.09831    1.59817  -0.687   0.4924    
## xGenderFemale         -9.54615    9.98431  -0.956   0.3396    
## xStudentYes          426.16715   16.73077  25.472  < 2e-16 ***
## xMarriedYes           -8.78055   10.36758  -0.847   0.3976    
## xEthnicityAsian       16.85752   14.12112   1.194   0.2333    
## xEthnicityCaucasian    9.29289   12.24194   0.759   0.4483    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 98.8 on 387 degrees of freedom
## Multiple R-squared:  0.9552, Adjusted R-squared:  0.9538 
## F-statistic: 687.7 on 12 and 387 DF,  p-value: < 2.2e-16
grid = 10^seq(10, -2, length = 100)
ridge_mod = glmnet(x, y, alpha = 0, lambda = grid)

dim(coef(ridge_mod))
## [1]  13 100
plot(ridge_mod) # wykres współczynników

ridge_mod$lambda[50] # Wyświetl 50-tą wartość lambdy
## [1] 11497.57
coef(ridge_mod)[,50] # Wyświetl współczynniki związane z 50-tą wartością lambdy
##        (Intercept)                 ID             Income              Limit 
##       4.437406e+02       7.098518e-04       2.072944e-01       6.255481e-03 
##             Rating              Cards                Age          Education 
##       9.352902e-02       1.094867e+00      -7.433736e-03      -3.648999e-02 
##       GenderFemale         StudentYes         MarriedYes     EthnicityAsian 
##       7.279655e-01       1.522470e+01      -2.740740e-01      -3.232180e-01 
## EthnicityCaucasian 
##      -8.954612e-02
sqrt(sum(coef(ridge_mod)[-1,50]^2)) # Oblicz normę l2
## [1] 15.28924
predict(ridge_mod, s = 50, type = "coefficients")[1:12,]
##    (Intercept)             ID         Income          Limit         Rating 
##  -387.01506763     0.02788993    -4.71033241     0.11026631     1.60846689 
##          Cards            Age      Education   GenderFemale     StudentYes 
##    16.10495492    -1.00880354    -0.40547730    -3.16706653   372.95067139 
##     MarriedYes EthnicityAsian 
##   -12.41062933    12.27125777
set.seed(1)

train = Credit %>%
  sample_frac(0.5)

test = Credit %>%
  setdiff(train)

x_train = model.matrix(Balance~., train)[,-1]
x_test = model.matrix(Balance~., test)[,-1]

y_train = train %>%
  select(Balance) %>%
  unlist() %>%
  as.numeric()

y_test = test %>%
  select(Balance) %>%
  unlist() %>%
  as.numeric()
ridge_mod = glmnet(x_train, y_train, alpha=0, lambda = grid, thresh = 1e-12)
ridge_pred = predict(ridge_mod, s = 4, newx = x_test)
mean((ridge_pred - y_test)^2)
## [1] 10293.67
ridge_pred = predict(ridge_mod, s = 1e10, newx = x_test)
mean((ridge_pred - y_test)^2)
## [1] 194030.9
ridge_pred = predict(ridge_mod, s = 0, newx = x_test)
mean((ridge_pred - y_test)^2)
## [1] 10660.7
set.seed(1)
cv.out = cv.glmnet(x_train, y_train, alpha = 0) # Dopasuj model regresji grzbietowej na danych treningowych
bestlam = cv.out$lambda.min  # Wybierz lamdę, która minimalizuje treningowy MSE 
bestlam
## [1] 41.60385
#Widzimy zatem, że wartość λ która powoduje najmniejszy błąd walidacji krzyżowej to 42
plot(cv.out) # Narysuj wykres treningowego MSE jako funkcję lambda

ridge_pred = predict(ridge_mod, s = bestlam, newx = x_test) # Użyj najlepszej lambdy do przewidywania danych testowych
mean((ridge_pred - y_test)^2) # Oblicz testowe MSE
## [1] 16116.16
out = glmnet(x, y, alpha = 0)
predict(out, type = "coefficients", s = bestlam)[1:13,]
##        (Intercept)                 ID             Income              Limit 
##      -402.69174822         0.02968823        -5.08362998         0.11363315 
##             Rating              Cards                Age          Education 
##         1.65055380        15.94593669        -0.97611320        -0.46045505 
##       GenderFemale         StudentYes         MarriedYes     EthnicityAsian 
##        -3.91626143       380.23436720       -12.29347706        13.01622062 
## EthnicityCaucasian 
##         8.53524735
lasso_mod = glmnet(x_train, 
                   y_train, 
                   alpha = 1, 
                   lambda = grid) # Dopasuj model lasso do danych treningowych

plot(lasso_mod)    # Wykreśl współczynniki
## Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
## collapsing to unique 'x' values

set.seed(1)
cv.out = cv.glmnet(x_train, y_train, alpha = 1) # Dopasuj model lasso do danych treningowych
plot(cv.out) # Narysuj wykres MSE dla próby uczącej jako funkcję lambda

bestlam = cv.out$lambda.min # Wybierz lamdę, która minimalizuje MSE w próbie uczącej
lasso_pred = predict(lasso_mod, s = bestlam, newx = x_test) # Użyj najlepszej lambdy do przewidywania danych testowych
mean((lasso_pred - y_test)^2) # Oblicz MSE w próbie testowej
## [1] 10506.25
out = glmnet(x, y, alpha = 1, lambda = grid) # Dopasuj model lasso do pełnego zbioru danych
lasso_coef = predict(out, type = "coefficients", s = bestlam)[1:13,] # Wyświetlanie współczynników przy użyciu lambda wybranego przez CV
lasso_coef
##        (Intercept)                 ID             Income              Limit 
##      -490.32404851         0.03266285        -7.67489924         0.17202850 
##             Rating              Cards                Age          Education 
##         1.38723835        16.02401187        -0.59472497        -0.72481881 
##       GenderFemale         StudentYes         MarriedYes     EthnicityAsian 
##        -7.47592875       421.49160417        -7.03306237        11.52382785 
## EthnicityCaucasian 
##         4.72253992

Aby zaliczyć to laboratorium, zamieść odpowiedzi na następujące pytania:

  • Który zbiór danych wybrałeś? - CREDIT

  • Jaka była Twoja zmienna zależna (tzn. co próbowałeś modelować)? - Średnie saldo karty kredytowej w $

  • Czy oczekiwałeś, że regresja grzbietowa będzie lepsza od lasso, czy odwrotnie? Jak wypada w stosunku do OLS? Pokaż odpowiednie raporty, miary dopasowania i krótko je omów (porównaj). Oczkekiwaliśmy, że lepszą będzie regresja grzbietowa, ze względu na możliwość występowania korelacji w predyktorach. W modelu OLS R^2 wyniosło 0,955 ( R-squared: 0.9552), co oznacza, że model jest bardzo dobrze dopasowany. MSE dla regresji grzbietowej wyniosi 16116, a dla regresji LASSO 10506, co stanowi mniejszą wartość. W przypadku regresji grzbietowej żaden ze wsppółczynnikow nie jest dokładnie zerowy, regresja ta nie dokonuje selekcji zmiennych. W przypadku LASSO można zauważyć, że występują współczynniki, które wynoszą dokładnie 0. Za model optymalny uznano zatem model regresji LASSO, ze względu na najniższą wartość MSE.

  • Które predyktory okazały się ważne w ostatecznym modelu (modelach)? Wszystkie predyktory okazały się być ważne, jednak najważniejszym było posiadanie statusu studenta.

LS0tDQp0aXRsZTogIk5pZWtsYXN5Y3puZSBtZXRvZHkgc3RhdHlzdHlraSINCmF1dGhvcjogIkZpbGlwIERyYWpza2ksIEtpbmdhIFdhc3pjenlrIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUNCiAgICBmb250c2l6ZTogMTBwdA0KICAgIHRvYzogeWVzDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KICAgIGRmX3ByaW50OiBkZWZhdWx0DQogICAgdG9jX2RlcHRoOiA1DQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzUnDQpzdWJ0aXRsZTogUmVndWxhcnl6YWNqYQ0KZWRpdG9yX29wdGlvbnM6DQogIG1hcmtkb3duOg0KICAgIHdyYXA6IDcyDQotLS0NCg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRX0NCmxpYnJhcnkoSVNMUikNCmxpYnJhcnkoZ2xtbmV0KQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpgYGANCg0KIyBUd29qYSBrb2xlaiENCg0KVGVyYXogbmFkc3plZMWCIGN6YXMgbmEgcHJ6ZXRlc3Rvd2FuaWUgdHljaCBtZXRvZCAocmVncmVzamEgZ3J6YmlldG93YSBpIGxhc3NvKSBvcmF6IG1ldG9kIG9jZW55ICh6ZXN0YXcgd2FsaWRhY3lqbnksIHdhbGlkYWNqYSBrcnp5xbxvd2EpIG5hIGlubnljaCB6YmlvcmFjaCBkYW55Y2guIE1vxbxlc3ogcHJhY293YcSHIHogemVzcG/FgmVtIG5hZCB0xIUgY3rEmcWbY2nEhSBsYWJvcmF0b3JpdW0uDQoNCk1vxbxlc3ogdcW8ecSHIGRvd29sbmVnbyB6YmlvcnUgZGFueWNoIHphd2FydGVnbyB3ICoqSVNMUioqIGx1YiB3eWJyYcSHIGplZGVuIHogcGFraWV0w7N3IGRhbnljaCBuYSBLYWdnbGUvRGF0YSBXb3JsZCBpdHAuICh6bWllbm5hIHphbGXFvG5hIG11c2kgYnnEhyBjacSFZ8WCYSkuIA0KDQpQb2JpZXJ6IHpiacOzciBkYW55Y2ggaSBzcHLDs2J1aiBva3JlxZtsacSHIG9wdHltYWxueSB6ZXN0YXcgcGFyYW1ldHLDs3csIGt0w7NyZSBuYWxlxbx5IHXFvHnEhyBkbyBqZWdvIG1vZGVsb3dhbmlhIQ0KDQpgYGB7cn0NCkNyZWRpdD1uYS5vbWl0KENyZWRpdCkNCnggPSBtb2RlbC5tYXRyaXgoQmFsYW5jZX4uLCBDcmVkaXQpWywtMV0gIyBwcnp5Y2luYW0gcGllcndzesSFIGtvbHVtbsSZDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgem9zdGF3aWFtIHByZWR5a3RvcnkNCnkgPSBDcmVkaXQgJT4lDQogIHNlbGVjdChCYWxhbmNlKSAlPiUNCiAgdW5saXN0KCkgJT4lDQogIGFzLm51bWVyaWMoKQ0KDQptb2RlbGxtIDwtIGxtKCB5fngpDQpzdW1tYXJ5KG1vZGVsbG0pDQpgYGANCmBgYHtyfQ0KZ3JpZCA9IDEwXnNlcSgxMCwgLTIsIGxlbmd0aCA9IDEwMCkNCnJpZGdlX21vZCA9IGdsbW5ldCh4LCB5LCBhbHBoYSA9IDAsIGxhbWJkYSA9IGdyaWQpDQoNCmRpbShjb2VmKHJpZGdlX21vZCkpDQpgYGANCmBgYHtyfQ0KcGxvdChyaWRnZV9tb2QpICMgd3lrcmVzIHdzcMOzxYJjenlubmlrw7N3DQpgYGANCg0KYGBge3J9DQpyaWRnZV9tb2QkbGFtYmRhWzUwXSAjIFd5xZt3aWV0bCA1MC10xIUgd2FydG/Fm8SHIGxhbWJkeQ0KYGBgDQoNCmBgYHtyfQ0KY29lZihyaWRnZV9tb2QpWyw1MF0gIyBXecWbd2lldGwgd3Nww7PFgmN6eW5uaWtpIHp3acSFemFuZSB6IDUwLXTEhSB3YXJ0b8WbY2nEhSBsYW1iZHkNCmBgYA0KDQpgYGB7cn0NCnNxcnQoc3VtKGNvZWYocmlkZ2VfbW9kKVstMSw1MF1eMikpICMgT2JsaWN6IG5vcm3EmSBsMg0KYGBgDQoNCmBgYHtyfQ0KcHJlZGljdChyaWRnZV9tb2QsIHMgPSA1MCwgdHlwZSA9ICJjb2VmZmljaWVudHMiKVsxOjEyLF0NCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQoNCnRyYWluID0gQ3JlZGl0ICU+JQ0KICBzYW1wbGVfZnJhYygwLjUpDQoNCnRlc3QgPSBDcmVkaXQgJT4lDQogIHNldGRpZmYodHJhaW4pDQoNCnhfdHJhaW4gPSBtb2RlbC5tYXRyaXgoQmFsYW5jZX4uLCB0cmFpbilbLC0xXQ0KeF90ZXN0ID0gbW9kZWwubWF0cml4KEJhbGFuY2V+LiwgdGVzdClbLC0xXQ0KDQp5X3RyYWluID0gdHJhaW4gJT4lDQogIHNlbGVjdChCYWxhbmNlKSAlPiUNCiAgdW5saXN0KCkgJT4lDQogIGFzLm51bWVyaWMoKQ0KDQp5X3Rlc3QgPSB0ZXN0ICU+JQ0KICBzZWxlY3QoQmFsYW5jZSkgJT4lDQogIHVubGlzdCgpICU+JQ0KICBhcy5udW1lcmljKCkNCmBgYA0KDQpgYGB7cn0NCnJpZGdlX21vZCA9IGdsbW5ldCh4X3RyYWluLCB5X3RyYWluLCBhbHBoYT0wLCBsYW1iZGEgPSBncmlkLCB0aHJlc2ggPSAxZS0xMikNCnJpZGdlX3ByZWQgPSBwcmVkaWN0KHJpZGdlX21vZCwgcyA9IDQsIG5ld3ggPSB4X3Rlc3QpDQptZWFuKChyaWRnZV9wcmVkIC0geV90ZXN0KV4yKQ0KYGBgDQoNCmBgYHtyfQ0KcmlkZ2VfcHJlZCA9IHByZWRpY3QocmlkZ2VfbW9kLCBzID0gMWUxMCwgbmV3eCA9IHhfdGVzdCkNCm1lYW4oKHJpZGdlX3ByZWQgLSB5X3Rlc3QpXjIpDQpgYGANCg0KYGBge3J9DQpyaWRnZV9wcmVkID0gcHJlZGljdChyaWRnZV9tb2QsIHMgPSAwLCBuZXd4ID0geF90ZXN0KQ0KbWVhbigocmlkZ2VfcHJlZCAtIHlfdGVzdCleMikNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQpjdi5vdXQgPSBjdi5nbG1uZXQoeF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAwKSAjIERvcGFzdWogbW9kZWwgcmVncmVzamkgZ3J6YmlldG93ZWogbmEgZGFueWNoIHRyZW5pbmdvd3ljaA0KYmVzdGxhbSA9IGN2Lm91dCRsYW1iZGEubWluICAjIFd5YmllcnogbGFtZMSZLCBrdMOzcmEgbWluaW1hbGl6dWplIHRyZW5pbmdvd3kgTVNFIA0KYmVzdGxhbQ0KDQojV2lkemlteSB6YXRlbSwgxbxlIHdhcnRvxZvEhyDOuyBrdMOzcmEgcG93b2R1amUgbmFqbW5pZWpzenkgYsWCxIVkIHdhbGlkYWNqaSBrcnp5xbxvd2VqIHRvIDQyDQpgYGANCg0KYGBge3J9DQpwbG90KGN2Lm91dCkgIyBOYXJ5c3VqIHd5a3JlcyB0cmVuaW5nb3dlZ28gTVNFIGpha28gZnVua2NqxJkgbGFtYmRhDQpgYGANCg0KYGBge3J9DQpyaWRnZV9wcmVkID0gcHJlZGljdChyaWRnZV9tb2QsIHMgPSBiZXN0bGFtLCBuZXd4ID0geF90ZXN0KSAjIFXFvHlqIG5hamxlcHN6ZWogbGFtYmR5IGRvIHByemV3aWR5d2FuaWEgZGFueWNoIHRlc3Rvd3ljaA0KbWVhbigocmlkZ2VfcHJlZCAtIHlfdGVzdCleMikgIyBPYmxpY3ogdGVzdG93ZSBNU0UNCmBgYA0KDQpgYGB7cn0NCm91dCA9IGdsbW5ldCh4LCB5LCBhbHBoYSA9IDApDQpwcmVkaWN0KG91dCwgdHlwZSA9ICJjb2VmZmljaWVudHMiLCBzID0gYmVzdGxhbSlbMToxMyxdDQpgYGANCg0KYGBge3J9DQpsYXNzb19tb2QgPSBnbG1uZXQoeF90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgeV90cmFpbiwgDQogICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCANCiAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBncmlkKSAjIERvcGFzdWogbW9kZWwgbGFzc28gZG8gZGFueWNoIHRyZW5pbmdvd3ljaA0KDQpwbG90KGxhc3NvX21vZCkgICAgIyBXeWtyZcWbbCB3c3DDs8WCY3p5bm5pa2kNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQpjdi5vdXQgPSBjdi5nbG1uZXQoeF90cmFpbiwgeV90cmFpbiwgYWxwaGEgPSAxKSAjIERvcGFzdWogbW9kZWwgbGFzc28gZG8gZGFueWNoIHRyZW5pbmdvd3ljaA0KcGxvdChjdi5vdXQpICMgTmFyeXN1aiB3eWtyZXMgTVNFIGRsYSBwcsOzYnkgdWN6xIVjZWogamFrbyBmdW5rY2rEmSBsYW1iZGENCmBgYA0KDQpgYGB7cn0NCmJlc3RsYW0gPSBjdi5vdXQkbGFtYmRhLm1pbiAjIFd5YmllcnogbGFtZMSZLCBrdMOzcmEgbWluaW1hbGl6dWplIE1TRSB3IHByw7NiaWUgdWN6xIVjZWoNCmxhc3NvX3ByZWQgPSBwcmVkaWN0KGxhc3NvX21vZCwgcyA9IGJlc3RsYW0sIG5ld3ggPSB4X3Rlc3QpICMgVcW8eWogbmFqbGVwc3plaiBsYW1iZHkgZG8gcHJ6ZXdpZHl3YW5pYSBkYW55Y2ggdGVzdG93eWNoDQptZWFuKChsYXNzb19wcmVkIC0geV90ZXN0KV4yKSAjIE9ibGljeiBNU0UgdyBwcsOzYmllIHRlc3Rvd2VqDQpgYGANCg0KDQpgYGB7cn0NCm91dCA9IGdsbW5ldCh4LCB5LCBhbHBoYSA9IDEsIGxhbWJkYSA9IGdyaWQpICMgRG9wYXN1aiBtb2RlbCBsYXNzbyBkbyBwZcWCbmVnbyB6YmlvcnUgZGFueWNoDQpsYXNzb19jb2VmID0gcHJlZGljdChvdXQsIHR5cGUgPSAiY29lZmZpY2llbnRzIiwgcyA9IGJlc3RsYW0pWzE6MTMsXSAjIFd5xZt3aWV0bGFuaWUgd3Nww7PFgmN6eW5uaWvDs3cgcHJ6eSB1xbx5Y2l1IGxhbWJkYSB3eWJyYW5lZ28gcHJ6ZXogQ1YNCmxhc3NvX2NvZWYNCmBgYA0KDQpBYnkgemFsaWN6ecSHIHRvIGxhYm9yYXRvcml1bSwgemFtaWXFm8SHIG9kcG93aWVkemkgbmEgbmFzdMSZcHVqxIVjZSBweXRhbmlhOg0KDQogLSBLdMOzcnkgemJpw7NyIGRhbnljaCB3eWJyYcWCZcWbPyAtIENSRURJVA0KIA0KIC0gSmFrYSBiecWCYSBUd29qYSB6bWllbm5hIHphbGXFvG5hICh0em4uIGNvIHByw7Nib3dhxYJlxZsgbW9kZWxvd2HEhyk/IC0gxZpyZWRuaWUgc2FsZG8ga2FydHkga3JlZHl0b3dlaiB3ICQNCiANCiAtIEN6eSBvY3pla2l3YcWCZcWbLCDFvGUgcmVncmVzamEgZ3J6YmlldG93YSBixJlkemllIGxlcHN6YSBvZCBsYXNzbywgY3p5IG9kd3JvdG5pZT8gSmFrIHd5cGFkYSB3IHN0b3N1bmt1IGRvIE9MUz8gUG9rYcW8IG9kcG93aWVkbmllIHJhcG9ydHksIG1pYXJ5IGRvcGFzb3dhbmlhIGkga3LDs3RrbyBqZSBvbcOzdyAocG9yw7N3bmFqKS4NCiBPY3prZWtpd2FsacWbbXksIMW8ZSBsZXBzesSFIGLEmWR6aWUgcmVncmVzamEgZ3J6YmlldG93YSwgemUgd3pnbMSZZHUgbmEgbW/FvGxpd2/Fm8SHIHd5c3TEmXBvd2FuaWEga29yZWxhY2ppIHcgcHJlZHlrdG9yYWNoLiBXIG1vZGVsdSBPTFMgUl4yIHd5bmlvc8WCbyAwLDk1NSAoIFItc3F1YXJlZDogIDAuOTU1MiksIGNvIG96bmFjemEsIMW8ZSBtb2RlbCBqZXN0IGJhcmR6byBkb2JyemUgZG9wYXNvd2FueS4gTVNFIGRsYSByZWdyZXNqaSBncnpiaWV0b3dlaiB3eW5pb3NpIDE2MTE2LCBhIGRsYSByZWdyZXNqaSBMQVNTTyAxMDUwNiwgY28gc3Rhbm93aSBtbmllanN6xIUgd2FydG/Fm8SHLiBXIHByenlwYWRrdSByZWdyZXNqaSBncnpiaWV0b3dlaiDFvGFkZW4gemUgd3NwcMOzxYJjenlubmlrb3cgbmllIGplc3QgZG9rxYJhZG5pZSB6ZXJvd3ksIHJlZ3Jlc2phIHRhIG5pZSBkb2tvbnVqZSBzZWxla2NqaSB6bWllbm55Y2guIFcgcHJ6eXBhZGt1IExBU1NPIG1vxbxuYSB6YXV3YcW8ecSHLCDFvGUgd3lzdMSZcHVqxIUgd3Nww7PFgmN6eW5uaWtpLCBrdMOzcmUgd3lub3N6xIUgZG9rxYJhZG5pZSAwLiBaYSBtb2RlbCBvcHR5bWFsbnkgdXpuYW5vIHphdGVtIG1vZGVsIHJlZ3Jlc2ppIExBU1NPLCB6ZSB3emdsxJlkdSBuYSBuYWpuacW8c3rEhSB3YXJ0b8WbxIcgTVNFLiANCiANCiAtIEt0w7NyZSBwcmVkeWt0b3J5IG9rYXphxYJ5IHNpxJkgd2HFvG5lIHcgb3N0YXRlY3pueW0gbW9kZWx1IChtb2RlbGFjaCk/DQpXc3p5c3RraWUgcHJlZHlrdG9yeSBva2F6YcWCeSBzacSZIGJ5xIcgd2HFvG5lLCBqZWRuYWsgbmFqd2HFvG5pZWpzenltIGJ5xYJvIHBvc2lhZGFuaWUgc3RhdHVzdSBzdHVkZW50YS4NCg0KDQoNCg==