主要議題:依圖形屬性辨認不同字型的英文字母

學習重點:

rm(list=ls(all=T))
options(digits=4, scipen=12)
library(dplyr)
library(rpart)
library(rpart.plot)
library(caret)
library(randomForest)
library(caTools)
source('DPP.R')

1. 兩類別模型

1.1 兩類別-底線模型

Let’s warm up by attempting to predict just whether a letter is B or not. To begin, load the file letters_ABPR.csv into R, and call it letters. Then, create a new variable isB in the dataframe, which takes the value “TRUE” if the observation corresponds to the letter B, and “FALSE” if it does not. You can do this by typing the following command into your R console:

letters\(isB = as.factor(letters\)letter == “B”)

Now split the data set into a training and testing set, putting 50% of the data in the training set. Set the seed to 1000 before making the split. The first argument to sample.split should be the dependent variable “letters$isB”. Remember that TRUE values from sample.split should go in the training set.

Before building models, let’s consider a baseline method that always predicts the most frequent outcome, which is “not B”. What is the accuracy of this baseline method on the test set?

D =  read.csv('data/letters_ABPR.csv')
D$isB = factor(D$letter=="B")
set.seed(1000)
spl = sample.split(D$isB, SplitRatio = 0.5)
TR = subset(D, spl)
TS = subset(D, !spl)
1 - mean(TS$isB == "TRUE")
[1] 0.7542
1.2 兩類別-決策樹模型

Now build a classification tree to predict whether a letter is a B or not, using the training set to build your model. Remember to remove the variable “letter” out of the model, as this is related to what we are trying to predict! To just remove one variable, you can either write out the other variables, or remember what we did in the Billboards problem in Week 3, and use the following notation:

m1 = rpart(isB ~ . - letter, TR, method='class')
prp(m1)

We are just using the default parameters in our CART model, so we don’t need to add the minbucket or cp arguments at all. We also added the argument method=“class” since this is a classification problem.

What is the accuracy of the CART model on the test set? (Use type=“class” when making predictions on the test set.)

pred = predict(m1, TS, type="class")
table(TS$isB, pred) %>% {sum(diag(.))/sum(.)}
[1] 0.9358
1.3 兩類別-隨機森林模型

Now, build a random forest model to predict whether the letter is a B or not (the isB variable) using the training set. You should use all of the other variables as independent variables, except letter (since it helped us define what we are trying to predict!). Use the default settings for ntree and nodesize (don’t include these arguments at all). Right before building the model, set the seed to 1000. (NOTE: You might get a slightly different answer on this problem, even if you set the random seed. This has to do with your operating system and the implementation of the random forest algorithm.)

What is the accuracy of the model on the test set?

set.seed(1000)
m2 = randomForest(isB ~ . - letter, TR)       # default settings
pred = predict(m2, TS, 'class')
table(TS$isB, pred) %>% {sum(diag(.))/sum(.)} # 
[1] 0.9865


2. 多類別模型

2.1 多類別-底線模型

Let us now move on to the problem that we were originally interested in, which is to predict whether or not a letter is one of the four letters A, B, P or R.

As we saw in the D2Hawkeye lecture, building a multiclass classification CART model in R is no harder than building the models for binary classification problems. Fortunately, building a random forest model is just as easy.

The variable in our data frame which we will be trying to predict is “letter”. Start by converting letter in the original data set (letters) to a factor by running the following command in R:

D$letter = factor(D$letter)

Now, generate new training and testing sets of the letters data frame using letters$letter as the first input to the sample.split function. Before splitting, set your seed to 2000. Again put 50% of the data in the training set. (Why do we need to split the data again? Remember that sample.split balances the outcome variable in the training and testing sets. With a new outcome variable, we want to re-generate our split.)

set.seed(1000)
spl = sample.split(D$letter, SplitRatio = 0.5)
TR = subset(D, spl)
TS = subset(D, !spl)

In a multiclass classification problem, a simple baseline model is to predict the most frequent class of all of the options.

What is the baseline accuracy on the testing set?

table(TS$letter) %>% prop.table %>% sort

     R      B      A      P 
0.2433 0.2458 0.2535 0.2574 
2.2 多類別-決策樹模型

Now build a classification tree to predict “letter”, using the training set to build your model. You should use all of the other variables as independent variables, except “isB”, since it is related to what we are trying to predict! Just use the default parameters in your CART model. Add the argument method=“class” since this is a classification problem. Even though we have multiple classes here, nothing changes in how we build the model from the binary case.

What is the test set accuracy of your CART model? Use the argument type=“class” when making predictions.

(HINT: When you are computing the test set accuracy using the confusion matrix, you want to add everything on the main diagonal and divide by the total number of observations in the test set, which can be computed with nrow(test), where test is the name of your test set).

m3 = rpart(letter ~ . - isB, TR, method='class')
prp(m3)

rpart.plot(m3)

pred = predict(m3, TS, type="class")
table(TS$letter, pred) %>% {sum(diag(.))/sum(.)}
[1] 0.878
2.3 多類別-隨機森林模型
set.seed(1000)
m4 = randomForest(letter ~ . - isB, TR)       
pred = predict(m4, TS, 'class')
table(TS$letter, pred) %>% {sum(diag(.))/sum(.)} 
[1] 0.982






LS0tDQp0aXRsZTogIkFTNC0yIOWtl+avjei+qOiqjSAodGVtcC4pIg0KYXV0aG9yOiAi5Y2T6ZuN54S2IEQ5OTQwMTAwMDEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQo8YnI+DQoNCioq5Li76KaB6K2w6aGM77ya5L6d5ZyW5b2i5bGs5oCn6L6o6KqN5LiN5ZCM5a2X5Z6L55qE6Iux5paH5a2X5q+NKioNCg0KKirlrbjnv5Lph43pu57vvJoqKg0KDQorIOWkmumhnuWIpeaxuuetluaoueaooeWeiw0KKyDpmqjmqZ/mo67mnpfnmoTpoJDmuKzog73lipsNCisg5rG6562W5qi5IHZzIOmaqOapn+ajruaelw0KDQpgYGB7ciBlY2hvPVQsIG1lc3NhZ2U9RiwgY2FjaGU9Riwgd2FybmluZz1GfQ0Kcm0obGlzdD1scyhhbGw9VCkpDQpvcHRpb25zKGRpZ2l0cz00LCBzY2lwZW49MTIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoY2FUb29scykNCnNvdXJjZSgnRFBQLlInKQ0KYGBgDQoNCi0gLSAtDQoNCiMjIyAxLiDlhanpoZ7liKXmqKHlnosgDQoNCiMjIyMjIDEuMSDlhanpoZ7liKUt5bqV57ea5qih5Z6LDQpMZXQncyB3YXJtIHVwIGJ5IGF0dGVtcHRpbmcgdG8gcHJlZGljdCBqdXN0IHdoZXRoZXIgYSBsZXR0ZXIgaXMgQiBvciBub3QuIFRvIGJlZ2luLCBsb2FkIHRoZSBmaWxlIGxldHRlcnNfQUJQUi5jc3YgaW50byBSLCBhbmQgY2FsbCBpdCBsZXR0ZXJzLiBUaGVuLCBjcmVhdGUgYSBuZXcgdmFyaWFibGUgaXNCIGluIHRoZSBkYXRhZnJhbWUsIHdoaWNoIHRha2VzIHRoZSB2YWx1ZSAiVFJVRSIgaWYgdGhlIG9ic2VydmF0aW9uIGNvcnJlc3BvbmRzIHRvIHRoZSBsZXR0ZXIgQiwgYW5kICJGQUxTRSIgaWYgaXQgZG9lcyBub3QuIFlvdSBjYW4gZG8gdGhpcyBieSB0eXBpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kIGludG8geW91ciBSIGNvbnNvbGU6DQoNCmxldHRlcnMkaXNCID0gYXMuZmFjdG9yKGxldHRlcnMkbGV0dGVyID09ICJCIikNCg0KTm93IHNwbGl0IHRoZSBkYXRhIHNldCBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0LCBwdXR0aW5nIDUwJSBvZiB0aGUgZGF0YSBpbiB0aGUgdHJhaW5pbmcgc2V0LiBTZXQgdGhlIHNlZWQgdG8gMTAwMCBiZWZvcmUgbWFraW5nIHRoZSBzcGxpdC4gVGhlIGZpcnN0IGFyZ3VtZW50IHRvIHNhbXBsZS5zcGxpdCBzaG91bGQgYmUgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAibGV0dGVycyRpc0IiLiBSZW1lbWJlciB0aGF0IFRSVUUgdmFsdWVzIGZyb20gc2FtcGxlLnNwbGl0IHNob3VsZCBnbyBpbiB0aGUgdHJhaW5pbmcgc2V0Lg0KDQpCZWZvcmUgYnVpbGRpbmcgbW9kZWxzLCBsZXQncyBjb25zaWRlciBhIGJhc2VsaW5lIG1ldGhvZCB0aGF0IGFsd2F5cyBwcmVkaWN0cyB0aGUgbW9zdCBmcmVxdWVudCBvdXRjb21lLCB3aGljaCBpcyAibm90IEIiLiBXaGF0IGlzIHRoZSBhY2N1cmFjeSBvZiB0aGlzIGJhc2VsaW5lIG1ldGhvZCBvbiB0aGUgdGVzdCBzZXQ/DQpgYGB7cn0NCkQgPSAgcmVhZC5jc3YoJ2RhdGEvbGV0dGVyc19BQlBSLmNzdicpDQpEJGlzQiA9IGZhY3RvcihEJGxldHRlcj09IkIiKQ0Kc2V0LnNlZWQoMTAwMCkNCnNwbCA9IHNhbXBsZS5zcGxpdChEJGlzQiwgU3BsaXRSYXRpbyA9IDAuNSkNClRSID0gc3Vic2V0KEQsIHNwbCkNClRTID0gc3Vic2V0KEQsICFzcGwpDQoxIC0gbWVhbihUUyRpc0IgPT0gIlRSVUUiKQ0KYGBgDQoNCiMjIyMjIDEuMiDlhanpoZ7liKUt5rG6562W5qi55qih5Z6LDQpOb3cgYnVpbGQgYSBjbGFzc2lmaWNhdGlvbiB0cmVlIHRvIHByZWRpY3Qgd2hldGhlciBhIGxldHRlciBpcyBhIEIgb3Igbm90LCB1c2luZyB0aGUgdHJhaW5pbmcgc2V0IHRvIGJ1aWxkIHlvdXIgbW9kZWwuIFJlbWVtYmVyIHRvIHJlbW92ZSB0aGUgdmFyaWFibGUgImxldHRlciIgb3V0IG9mIHRoZSBtb2RlbCwgYXMgdGhpcyBpcyByZWxhdGVkIHRvIHdoYXQgd2UgYXJlIHRyeWluZyB0byBwcmVkaWN0ISBUbyBqdXN0IHJlbW92ZSBvbmUgdmFyaWFibGUsIHlvdSBjYW4gZWl0aGVyIHdyaXRlIG91dCB0aGUgb3RoZXIgdmFyaWFibGVzLCBvciByZW1lbWJlciB3aGF0IHdlIGRpZCBpbiB0aGUgQmlsbGJvYXJkcyBwcm9ibGVtIGluIFdlZWsgMywgYW5kIHVzZSB0aGUgZm9sbG93aW5nIG5vdGF0aW9uOg0KDQpgYGB7cn0NCm0xID0gcnBhcnQoaXNCIH4gLiAtIGxldHRlciwgVFIsIG1ldGhvZD0nY2xhc3MnKQ0KcHJwKG0xKQ0KYGBgDQpXZSBhcmUganVzdCB1c2luZyB0aGUgZGVmYXVsdCBwYXJhbWV0ZXJzIGluIG91ciBDQVJUIG1vZGVsLCBzbyB3ZSBkb24ndCBuZWVkIHRvIGFkZCB0aGUgbWluYnVja2V0IG9yIGNwIGFyZ3VtZW50cyBhdCBhbGwuIFdlIGFsc28gYWRkZWQgdGhlIGFyZ3VtZW50IG1ldGhvZD0iY2xhc3MiIHNpbmNlIHRoaXMgaXMgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLg0KDQpXaGF0IGlzIHRoZSBhY2N1cmFjeSBvZiB0aGUgQ0FSVCBtb2RlbCBvbiB0aGUgdGVzdCBzZXQ/IChVc2UgdHlwZT0iY2xhc3MiIHdoZW4gbWFraW5nIHByZWRpY3Rpb25zIG9uIHRoZSB0ZXN0IHNldC4pDQpgYGB7cn0NCnByZWQgPSBwcmVkaWN0KG0xLCBUUywgdHlwZT0iY2xhc3MiKQ0KdGFibGUoVFMkaXNCLCBwcmVkKSAlPiUge3N1bShkaWFnKC4pKS9zdW0oLil9DQpgYGANCg0KIyMjIyMgMS4zIOWFqemhnuWIpS3pmqjmqZ/mo67mnpfmqKHlnosNCk5vdywgYnVpbGQgYSByYW5kb20gZm9yZXN0IG1vZGVsIHRvIHByZWRpY3Qgd2hldGhlciB0aGUgbGV0dGVyIGlzIGEgQiBvciBub3QgKHRoZSBpc0IgdmFyaWFibGUpIHVzaW5nIHRoZSB0cmFpbmluZyBzZXQuIFlvdSBzaG91bGQgdXNlIGFsbCBvZiB0aGUgb3RoZXIgdmFyaWFibGVzIGFzIGluZGVwZW5kZW50IHZhcmlhYmxlcywgZXhjZXB0IGxldHRlciAoc2luY2UgaXQgaGVscGVkIHVzIGRlZmluZSB3aGF0IHdlIGFyZSB0cnlpbmcgdG8gcHJlZGljdCEpLiBVc2UgdGhlIGRlZmF1bHQgc2V0dGluZ3MgZm9yIG50cmVlIGFuZCBub2Rlc2l6ZSAoZG9uJ3QgaW5jbHVkZSB0aGVzZSBhcmd1bWVudHMgYXQgYWxsKS4gUmlnaHQgYmVmb3JlIGJ1aWxkaW5nIHRoZSBtb2RlbCwgc2V0IHRoZSBzZWVkIHRvIDEwMDAuIChOT1RFOiBZb3UgbWlnaHQgZ2V0IGEgc2xpZ2h0bHkgZGlmZmVyZW50IGFuc3dlciBvbiB0aGlzIHByb2JsZW0sIGV2ZW4gaWYgeW91IHNldCB0aGUgcmFuZG9tIHNlZWQuIFRoaXMgaGFzIHRvIGRvIHdpdGggeW91ciBvcGVyYXRpbmcgc3lzdGVtIGFuZCB0aGUgaW1wbGVtZW50YXRpb24gb2YgdGhlIHJhbmRvbSBmb3Jlc3QgYWxnb3JpdGhtLikNCg0KV2hhdCBpcyB0aGUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsIG9uIHRoZSB0ZXN0IHNldD8NCmBgYHtyfQ0Kc2V0LnNlZWQoMTAwMCkNCm0yID0gcmFuZG9tRm9yZXN0KGlzQiB+IC4gLSBsZXR0ZXIsIFRSKSAgICAgICAjIGRlZmF1bHQgc2V0dGluZ3MNCnByZWQgPSBwcmVkaWN0KG0yLCBUUywgJ2NsYXNzJykNCnRhYmxlKFRTJGlzQiwgcHJlZCkgJT4lIHtzdW0oZGlhZyguKSkvc3VtKC4pfSAjIA0KYGBgDQo8YmE+DQoNCi0gLSAtDQoNCiMjIyAyLiDlpJrpoZ7liKXmqKHlnosNCg0KIyMjIyMgMi4xIOWkmumhnuWIpS3lupXnt5rmqKHlnosgDQpMZXQgdXMgbm93IG1vdmUgb24gdG8gdGhlIHByb2JsZW0gdGhhdCB3ZSB3ZXJlIG9yaWdpbmFsbHkgaW50ZXJlc3RlZCBpbiwgd2hpY2ggaXMgdG8gcHJlZGljdCB3aGV0aGVyIG9yIG5vdCBhIGxldHRlciBpcyBvbmUgb2YgdGhlIGZvdXIgbGV0dGVycyBBLCBCLCBQIG9yIFIuDQoNCkFzIHdlIHNhdyBpbiB0aGUgRDJIYXdrZXllIGxlY3R1cmUsIGJ1aWxkaW5nIGEgbXVsdGljbGFzcyBjbGFzc2lmaWNhdGlvbiBDQVJUIG1vZGVsIGluIFIgaXMgbm8gaGFyZGVyIHRoYW4gYnVpbGRpbmcgdGhlIG1vZGVscyBmb3IgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW1zLiBGb3J0dW5hdGVseSwgYnVpbGRpbmcgYSByYW5kb20gZm9yZXN0IG1vZGVsIGlzIGp1c3QgYXMgZWFzeS4NCg0KVGhlIHZhcmlhYmxlIGluIG91ciBkYXRhIGZyYW1lIHdoaWNoIHdlIHdpbGwgYmUgdHJ5aW5nIHRvIHByZWRpY3QgaXMgImxldHRlciIuIFN0YXJ0IGJ5IGNvbnZlcnRpbmcgbGV0dGVyIGluIHRoZSBvcmlnaW5hbCBkYXRhIHNldCAobGV0dGVycykgdG8gYSBmYWN0b3IgYnkgcnVubmluZyB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gUjoNCmBgYHtyfQ0KRCRsZXR0ZXIgPSBmYWN0b3IoRCRsZXR0ZXIpDQpgYGANCg0KTm93LCBnZW5lcmF0ZSBuZXcgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyBvZiB0aGUgbGV0dGVycyBkYXRhIGZyYW1lIHVzaW5nIGxldHRlcnMkbGV0dGVyIGFzIHRoZSBmaXJzdCBpbnB1dCB0byB0aGUgc2FtcGxlLnNwbGl0IGZ1bmN0aW9uLiBCZWZvcmUgc3BsaXR0aW5nLCBzZXQgeW91ciBzZWVkIHRvIDIwMDAuIEFnYWluIHB1dCA1MCUgb2YgdGhlIGRhdGEgaW4gdGhlIHRyYWluaW5nIHNldC4gKFdoeSBkbyB3ZSBuZWVkIHRvIHNwbGl0IHRoZSBkYXRhIGFnYWluPyBSZW1lbWJlciB0aGF0IHNhbXBsZS5zcGxpdCBiYWxhbmNlcyB0aGUgb3V0Y29tZSB2YXJpYWJsZSBpbiB0aGUgdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cy4gV2l0aCBhIG5ldyBvdXRjb21lIHZhcmlhYmxlLCB3ZSB3YW50IHRvIHJlLWdlbmVyYXRlIG91ciBzcGxpdC4pDQpgYGB7cn0NCnNldC5zZWVkKDEwMDApDQpzcGwgPSBzYW1wbGUuc3BsaXQoRCRsZXR0ZXIsIFNwbGl0UmF0aW8gPSAwLjUpDQpUUiA9IHN1YnNldChELCBzcGwpDQpUUyA9IHN1YnNldChELCAhc3BsKQ0KYGBgDQoNCkluIGEgbXVsdGljbGFzcyBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLCBhIHNpbXBsZSBiYXNlbGluZSBtb2RlbCBpcyB0byBwcmVkaWN0IHRoZSBtb3N0IGZyZXF1ZW50IGNsYXNzIG9mIGFsbCBvZiB0aGUgb3B0aW9ucy4NCg0KV2hhdCBpcyB0aGUgYmFzZWxpbmUgYWNjdXJhY3kgb24gdGhlIHRlc3Rpbmcgc2V0Pw0KYGBge3J9DQp0YWJsZShUUyRsZXR0ZXIpICU+JSBwcm9wLnRhYmxlICU+JSBzb3J0DQpgYGANCg0KIyMjIyMgMi4yIOWkmumhnuWIpS3msbrnrZbmqLnmqKHlnosNCk5vdyBidWlsZCBhIGNsYXNzaWZpY2F0aW9uIHRyZWUgdG8gcHJlZGljdCAibGV0dGVyIiwgdXNpbmcgdGhlIHRyYWluaW5nIHNldCB0byBidWlsZCB5b3VyIG1vZGVsLiBZb3Ugc2hvdWxkIHVzZSBhbGwgb2YgdGhlIG90aGVyIHZhcmlhYmxlcyBhcyBpbmRlcGVuZGVudCB2YXJpYWJsZXMsIGV4Y2VwdCAiaXNCIiwgc2luY2UgaXQgaXMgcmVsYXRlZCB0byB3aGF0IHdlIGFyZSB0cnlpbmcgdG8gcHJlZGljdCEgSnVzdCB1c2UgdGhlIGRlZmF1bHQgcGFyYW1ldGVycyBpbiB5b3VyIENBUlQgbW9kZWwuIEFkZCB0aGUgYXJndW1lbnQgbWV0aG9kPSJjbGFzcyIgc2luY2UgdGhpcyBpcyBhIGNsYXNzaWZpY2F0aW9uIHByb2JsZW0uIEV2ZW4gdGhvdWdoIHdlIGhhdmUgbXVsdGlwbGUgY2xhc3NlcyBoZXJlLCBub3RoaW5nIGNoYW5nZXMgaW4gaG93IHdlIGJ1aWxkIHRoZSBtb2RlbCBmcm9tIHRoZSBiaW5hcnkgY2FzZS4NCg0KV2hhdCBpcyB0aGUgdGVzdCBzZXQgYWNjdXJhY3kgb2YgeW91ciBDQVJUIG1vZGVsPyBVc2UgdGhlIGFyZ3VtZW50IHR5cGU9ImNsYXNzIiB3aGVuIG1ha2luZyBwcmVkaWN0aW9ucy4NCg0KKEhJTlQ6IFdoZW4geW91IGFyZSBjb21wdXRpbmcgdGhlIHRlc3Qgc2V0IGFjY3VyYWN5IHVzaW5nIHRoZSBjb25mdXNpb24gbWF0cml4LCB5b3Ugd2FudCB0byBhZGQgZXZlcnl0aGluZyBvbiB0aGUgbWFpbiBkaWFnb25hbCBhbmQgZGl2aWRlIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSB0ZXN0IHNldCwgd2hpY2ggY2FuIGJlIGNvbXB1dGVkIHdpdGggbnJvdyh0ZXN0KSwgd2hlcmUgdGVzdCBpcyB0aGUgbmFtZSBvZiB5b3VyIHRlc3Qgc2V0KS4NCmBgYHtyfQ0KbTMgPSBycGFydChsZXR0ZXIgfiAuIC0gaXNCLCBUUiwgbWV0aG9kPSdjbGFzcycpDQpwcnAobTMpDQpgYGANCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTl9DQpycGFydC5wbG90KG0zKQ0KYGBgDQpgYGB7cn0NCnByZWQgPSBwcmVkaWN0KG0zLCBUUywgdHlwZT0iY2xhc3MiKQ0KdGFibGUoVFMkbGV0dGVyLCBwcmVkKSAlPiUge3N1bShkaWFnKC4pKS9zdW0oLil9DQpgYGANCg0KIyMjIyMgMi4zIOWkmumhnuWIpS3pmqjmqZ/mo67mnpfmqKHlnosNCmBgYHtyfQ0Kc2V0LnNlZWQoMTAwMCkNCm00ID0gcmFuZG9tRm9yZXN0KGxldHRlciB+IC4gLSBpc0IsIFRSKSAgICAgICANCnByZWQgPSBwcmVkaWN0KG00LCBUUywgJ2NsYXNzJykNCnRhYmxlKFRTJGxldHRlciwgcHJlZCkgJT4lIHtzdW0oZGlhZyguKSkvc3VtKC4pfSANCmBgYA0KDQoNCi0gLSAtDQoNCjxicj48YnI+PGJyPjxicj48YnI+DQoNCjxzdHlsZT4NCi5jYXB0aW9uIHsNCiAgY29sb3I6ICM3Nzc7DQogIG1hcmdpbi10b3A6IDEwcHg7DQp9DQpwIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnByZSB7DQogIHdvcmQtYnJlYWs6IG5vcm1hbDsNCiAgd29yZC13cmFwOiBub3JtYWw7DQogIGxpbmUtaGVpZ2h0OiAxOw0KfQ0KcHJlIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KLnJ7DQogIGxpbmUtaGVpZ2h0OiAxLjI7DQp9DQoNCnRpdGxlew0KICBjb2xvcjogI2NjMDAwMDsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmJvZHl7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoMSxoMixoMyxoNCxoNXsNCiAgY29sb3I6ICMwMDY2ZmY7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpoNCxoNXsNCiAgYmFja2dyb3VuZDogI2NjZmZmZjsNCn0NCg0KPC9zdHlsZT4NCg0KDQoNCg0KDQoNCg==