Skip to content

Instantly share code, notes, and snippets.

@kevindavenport
Last active March 24, 2016 03:49
Show Gist options
  • Save kevindavenport/11157249 to your computer and use it in GitHub Desktop.
Save kevindavenport/11157249 to your computer and use it in GitHub Desktop.
A Practical Introduction to Information Entropy
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "",
"signature": "sha256:3338a1daefbcc568033180ef3e8f67027da32f0fd4d4ffbfeffdfc2149397bc1"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A Practical Introduction to Information Entropy blog post at http://kldavenport.com\n",
"\n"
]
},
{
"cell_type": "heading",
"level": 3,
"metadata": {},
"source": [
"Information Entropy to Detecting Randomly Generated Domains"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The intent of this post is to generally explore information entropy applied to a toy problem in network security. I'll outline a common problem then the basic concepts of entropy to then show a practical implementation using the the Kullback-Leibler divergence and the Python data stack.\n",
"\n",
"In network security the latest malware botnet threat paradigm utilizes peer-to-peer (P2P) communication methods and domain generating algorithms (DGAs). This method avoids any single point of failure and evades many countermeasures as the command and control framework is embedded in the botnets themselves instead of the outdated paradigm of relying on external servers. \n",
"\n",
"A potential method of minimizing the impact of these threats is imploying a profiler that detects attributes consistent with DGA and P2P."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Defining Entropy**\n",
"\n",
"Entropy is the average amount or expectation of information gained for each possible event $X_i$ with possibility $P(X_i)$ that is represented by $log_2 (1/P(X_i))$ bits: $$H(X)=\\sum_{j=1}^n P(X_j) \\log_2 (1/P(X_j))=-\\sum_{j=1}^n P(X_j) \\log_2 P(X_j)$$\n",
"where $H(X)$ is the entropy of events (i.e., random variable) $X$. Entropy is the amount of uncertainty or degree of surprise in an information system, thus low probability leads to high amount of information. Consider two events probabilities $p$ and $1-p$. The average information (per symbol or event) is:\n",
"\n",
"$$H(X)=-p log_2p-(1-p)log_2 (1-p)$$\n",
"\n",
"Entropy is at it's greatest when all events/outcomes are equally likely to occur.\n",
"\n",
"* High entropy means $X$ is from a uniform distribution, where every outcome is as likely to occur\n",
" \n",
"* Low entropy means $X$ is from varied distribution (with various peaks and valleys)\n",
" \n",
"Claude E. Shannon's outlined methods of estimating the entropy of English in his paper <a href =\"https://www.princeton.edu/~wbialek/rome/refs/shannon_51.pdf\"> \"Prediction and Entropy of Printed English\"</a>. He states that constraints imposed on the text of English language decrease in its overall entropy. Constraints such as 'q' must always be followed by 'u and other rules such as \"i before e except after c\" are dependencies that make the English language more redundant. This redundancy is beneficial when discerning a signal from noise. If you read \"John is embroiled in a legal qxxxdary\" you could quickly infer 'uan' as the missing letters. W \n",
"\n",
"For the toy problem outlined above, we can calculate the entropy of English using N-grams by statistically calculating the entropy of the next letter when the previous N - 1 letters are known. The entropy approaches H (entropy of English) as N increases."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Below is my favorite intuitive explanation of Information Entropy:**"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from IPython.display import YouTubeVideo\n",
"YouTubeVideo('R4OlXb9aTvQ')"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"\n",
" <iframe\n",
" width=\"400\"\n",
" height=300\"\n",
" src=\"https://www.youtube.com/embed/R4OlXb9aTvQ\"\n",
" frameborder=\"0\"\n",
" allowfullscreen\n",
" ></iframe>\n",
" "
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 1,
"text": [
"<IPython.lib.display.YouTubeVideo at 0x102fc4cd0>"
]
}
],
"prompt_number": 1
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import pandas as pd\n",
"import numpy as np\n",
"import seaborn as sns\n",
"%pylab inline"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Populating the interactive namespace from numpy and matplotlib\n"
]
},
{
"output_type": "stream",
"stream": "stderr",
"text": [
"Vendor: Continuum Analytics, Inc.\n",
"Package: mkl\n",
"Message: trial mode expires in 26 days\n"
]
}
],
"prompt_number": 2
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As I build the model I'll start with a 2k row subset of Alexa's top 1,000,000 sites world wide. I will utilize the full dataset at the end.\n",
"\n",
"In bash:\n",
"```shell\n",
"# Create subset file\n",
"head -n 2000 top1m.csv > top2k.csv\n",
"# If we wanted to add a header before hand\n",
"sed -i 1i\"rank,url\" top2k.csv\n",
"\n",
"```\n",
"OR use *nrows=20* parameter in read_csv\n"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"alexadf = pd.read_csv('top2k.csv',names=['uri'],usecols= [1],header=None,encoding='utf-8')\n",
"alexadf.head(2)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uri</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> google.com</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> facebook.com</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>2 rows \u00d7 1 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
" uri\n",
"0 google.com\n",
"1 facebook.com\n",
"\n",
"[2 rows x 1 columns]"
]
}
],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the name www.google.com, the TLD (top-level domain) is the dot(.) and the proceeding *com*. for complex domain-names using com.cn and .com.tw, the text after the last dot is the TLD. For this simple first pass model we won't weigh or penalize domain names belonging to less trusted countries/regions. We also won't consider cobinations of sub-domains.\n",
"####\"Normal\" domain names\n",
"To start we'll need to extract all domain names before the first '.' then drop NA and duplicates from the dataframe. We can check for duplicates by comparing the len() to nunique()."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"alexadf['uri'] = alexadf['uri'].apply(lambda x: x[:x.find('.')]).astype(str)\n",
"alexadf = alexadf.dropna()\n",
"alexadf = alexadf.drop_duplicates()\n",
"alexadf['label'] = 0 # Create label identifying values as nominal\n",
"alexadf.head(2)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uri</th>\n",
" <th>label</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> google</td>\n",
" <td> 0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> facebook</td>\n",
" <td> 0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>2 rows \u00d7 2 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 4,
"text": [
" uri label\n",
"0 google 0\n",
"1 facebook 0\n",
"\n",
"[2 rows x 2 columns]"
]
}
],
"prompt_number": 4
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"####\"Abnormal\" domain names\n",
"We can acquire a list of known suspicious domains here: https://isc.sans.edu/suspicious_domains.html\n",
"to start we'll use the 'High Sensitivity Level' file. These domain names are probably not as sophisticated as those created by something like *Kwyjibo* http://dl.acm.org/citation.cfm?id=1455471 and more in line with domains generated by Con\ufb01cker and Kraken."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"malicious_domains = pd.read_csv('suspiciousdomains_High.txt',encoding='utf-8',skiprows=17,header=None,nrows=2200)\n",
"malicious_domains.columns = ['uri']\n",
"malicious_domains['uri'] = malicious_domains['uri'].apply(lambda x: x[:x.find('.')]).astype(str)\n",
"\n",
"malicious_domains.dropna()\n",
"malicious_domains.drop_duplicates()\n",
"\n",
"malicious_domains['label'] = 1\n",
"\n",
"malicious_domains.head(2)\n",
"#malicious_domains.info()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uri</th>\n",
" <th>label</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> 000007</td>\n",
" <td> 1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 000cc</td>\n",
" <td> 1</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>2 rows \u00d7 2 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 5,
"text": [
" uri label\n",
"0 000007 1\n",
"1 000cc 1\n",
"\n",
"[2 rows x 2 columns]"
]
}
],
"prompt_number": 5
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Concatenate normal and dga dataframes into one dataframe and create a label column:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"df = pd.concat([alexadf, malicious_domains], ignore_index=True)\n",
"df['length'] = [len(x) for x in df['uri']]\n",
"\n",
"df.head(2)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uri</th>\n",
" <th>label</th>\n",
" <th>length</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> google</td>\n",
" <td> 0</td>\n",
" <td> 6</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> facebook</td>\n",
" <td> 0</td>\n",
" <td> 8</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>2 rows \u00d7 3 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 6,
"text": [
" uri label length\n",
"0 google 0 6\n",
"1 facebook 0 8\n",
"\n",
"[2 rows x 3 columns]"
]
}
],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll implement Entropy defined as $H(X)=\\sum_{j=1}^n P(X_j) \\log_2 (1/P(X_j))=-\\sum_{j=1}^n P(X_j) \\log_2 P(X_j)$ (same as above) using the Counter dict subclass that counts hashable objects. It is an unordered collection where elements are stored as dict keys and corresponding counts are stored as dict values. Counts are allowed to be any int value including zero or negative counts. This class is similar to 'bags' in C++. "
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from collections import Counter\n",
"# Remember mixed standard python functions and numpy functions are very slow \n",
"def calcEntropy(x):\n",
" p, lens = Counter(x), np.float(len(x))\n",
" return -np.sum( count/lens * np.log2(count/lens) for count in p.values())"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 7
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's take a look at the top 5 entropy values:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"df['entropy'] = [calcEntropy(x) for x in df['uri']]\n",
"df.sort('entropy', ascending = False)[:5]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uri</th>\n",
" <th>label</th>\n",
" <th>length</th>\n",
" <th>entropy</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2582</th>\n",
" <td> cg79wo20kl92doowfn01oqpo9mdieowv5tyj</td>\n",
" <td> 1</td>\n",
" <td> 36</td>\n",
" <td> 4.308271</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3506</th>\n",
" <td> hfoajof1ornmzmasvuqiowdpchap</td>\n",
" <td> 1</td>\n",
" <td> 28</td>\n",
" <td> 4.066109</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2718</th>\n",
" <td> crovniedelamjdusaboye73</td>\n",
" <td> 1</td>\n",
" <td> 23</td>\n",
" <td> 4.055958</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2576</th>\n",
" <td> cerovskiprijatnomnebi25</td>\n",
" <td> 1</td>\n",
" <td> 23</td>\n",
" <td> 3.969002</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3278</th>\n",
" <td> gesticulez-rondkijkt</td>\n",
" <td> 1</td>\n",
" <td> 20</td>\n",
" <td> 3.921928</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>5 rows \u00d7 4 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 8,
"text": [
" uri label length entropy\n",
"2582 cg79wo20kl92doowfn01oqpo9mdieowv5tyj 1 36 4.308271\n",
"3506 hfoajof1ornmzmasvuqiowdpchap 1 28 4.066109\n",
"2718 crovniedelamjdusaboye73 1 23 4.055958\n",
"2576 cerovskiprijatnomnebi25 1 23 3.969002\n",
"3278 gesticulez-rondkijkt 1 20 3.921928\n",
"\n",
"[5 rows x 4 columns]"
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can see that the length and entropy of domains of label 1 (randomly generated) are higher. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use the Pearson's coefficient to quantify the association between two variables that are assumed to be from a Gaussian distribution. The underlying distribution of length and entropy could be power or box-cox transformed to meet the assumption of normality, but knowing that the measures are linear (not monotonic) will be good enough for now. Let's try Spearman's coefficient just to compare the difference between coefficients."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from scipy import stats\n",
"#plt.rcParams['figure.figsize'] = 13, 7\n",
"sns.set_context(rc={\"figure.figsize\": (7, 5)})\n",
"\n",
"g = sns.JointGrid(df.length.astype(float), df.entropy.astype(float))\n",
"g.plot(sns.regplot, sns.distplot, stats.spearmanr);\n",
"\n",
"print \"Pearson's r: {0}\".format(stats.pearsonr(df.length.astype(float), df.entropy.astype(float)))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Pearson's r: (0.80826357554406247, 0.0)\n"
]
},
{
"metadata": {},
"output_type": "display_data",
"png": "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\nQgihGf8f174pDFbLw4gAAAAASUVORK5CYII=\n",
"text": [
"<matplotlib.figure.Figure at 0x1125af110>"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can compare the distribution of entropy for normal vs malcious domains via histograms and parametric statistics. More robust non-parametric methods can be used for the next model iteration."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"sns.set_context(rc={\"figure.figsize\": (7, 5)})\n",
"\n",
"\n",
"dfNominal = df[df['label']== 0]\n",
"dfDGA = df[df['label']== 1]\n",
"\n",
"def shadedHist(df,col,bins):\n",
" df[col].hist(bins = bins, color = 'dodgerblue', alpha = .6, normed = False)\n",
" len_mean = df[col].mean()\n",
" len_std = df[col].std()\n",
" # mpl\n",
" plt.plot([len_mean, len_mean], [0,2500 ],'k-',lw=3,color = 'black',alpha = .4)\n",
" plt.plot([len_mean + (2 * len_std), len_mean + (2 * len_std)], [0, 2500], 'k-', lw=2, color = 'red', alpha = .4)\n",
" plt.axvspan(len_mean + (2 * len_std), max(df[col]), facecolor='r', alpha=0.3)\n",
" plt.title(col)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Red highlighting is of values over 2 standard deviations from the mean:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Nominal entropy distribution**"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"sns.set_context(rc={\"figure.figsize\": (7, 5)})\n",
"\n",
"shadedHist(df[df['label']== 0],'entropy',10) \n",
"\n",
"nominal_parametric_upper = dfNominal['entropy'].mean() + \\\n",
" 2 * dfNominal['entropy'].std()\n",
" \n",
"print nominal_parametric_upper"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"3.66256510371\n"
]
},
{
"metadata": {},
"output_type": "display_data",
"png": "iVBORw0KGgoAAAANSUhEUgAAAbgAAAFCCAYAAACDw8jMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAHKlJREFUeJzt3X9wVOWh//HPJptlYbOBAsHeuWhoqSCOTTopsYISGEcU\nRzqiGDEbggM6WjRqycAQCDQYf4A6TIoXCtXp1E6k/HBIHeporVUwDsHqgKKCxl6s+q3wtbtQYXeT\n3Q3uuX9oVmJg88NsNufh/fprc87Z3efJo7z3JCe7DsuyLAEAYJiMdA8AAIBUIHAAACMROACAkQgc\nAMBIBA4AYCQCBwAwEoED0uiZZ57RH//4x3QPAzASgQPSaN++fYpEIukeBmAkZ7oHAJjklVde0aZN\nm9TW1ia3262lS5fqtdde02effSa/368jR45o+PDhqqur04EDB7Rr1y41NTVp0KBBOn78uN5++235\n/X5ddNFFevjhh7V69Wq9/vrrysjIUEFBgZYtWyaPx6Mrr7xSV111lfbt26dgMKj58+ertLRUK1as\n0IgRI7Ro0SJJ0s6dO/XXv/5V69evT/N3Buh/nMEBfeTjjz9WXV2dnnzySf3pT39SbW2tKioq1Nra\nqn379unxxx/XCy+8oJycHG3btk3Tp0/XlVdeqfnz56usrEySdPToUT377LN69NFH9Zvf/EZ+v187\nd+7Uzp07FY/H9eijjyaer6WlRTt27FB9fb0ef/xxffjhh5o7d64aGhoUj8clSdu2bVNpaWlavh9A\nunEGB/SRPXv2yO/369Zbb01sy8zM1Keffqqf/exn8ng8kqSLL75YJ06cSBxz+rvlFRQUKCPjq9ed\nr732miorK5WZmSlJKi8v19133504tj2K5513nqZMmaI9e/Zo/vz5Gj16tHbt2qUxY8bI7/fr8ssv\nT92kgQGMwAF9xLIsTZo0SXV1dYltR44c0TPPPKOTJ08mtjkcjg73a//a4XBoyJAhie3xeLxD/L78\n8ku1tbUlvm4PYfu+9hCWlZVpx44dGjNmjObMmdNHswPshx9RAn3ksssu0549e/TRRx9J+uoMbNas\nWYpGox2OsywrEa7MzMxEtL79vudXXHGFtm7dqlOnTikej2vz5s264oorEvufffZZSV9FtKmpScXF\nxZKka665Ru+//75eeuklzZ49OzWTBWyAMzigj/zoRz9SbW2tKisrZVmWnE6nNm7cqL179yoWiyWO\n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\nPzWU1dFn18zkAAAAAElFTkSuQmCC\n",
"text": [
"<matplotlib.figure.Figure at 0x1122afa90>"
]
}
],
"prompt_number": 11
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Malicious entropy distribution**"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"sns.set_context(rc={\"figure.figsize\": (7, 5)})\n",
"\n",
"shadedHist(dfDGA,'entropy',10)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\n",
"text": [
"<matplotlib.figure.Figure at 0x1122ac610>"
]
}
],
"prompt_number": 12
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**ngrams**\n",
"\n",
"The differences based on entropy alone probably won't add enough distinguishing or predictive power in determining nominal vs DGA domains so let's try to engineer more features. Character n-grams avoid the use of tokenizers and are more language agnostic than word-grams, but they increase the dimensionality of the data.\n",
"\n",
"We can utilize scikit-learn's CountVectorizer to tokenize and count the word occurrences of the nominal Alexa domain name corpus of text. We will tune the following parameters:\n",
"\n",
"- The *min_df* parameter will disregard words which appear less than *min_df* fraction of reviews.\n",
"\n",
"- The *ngram_range* sets the lower and upper boundary on the range of n-values for the n-grams to be extracted.\n",
"\n",
"We calculated the upper entropy limit for Alexa values (label = 0) to be 3.66 bits so we'll set the range between 3 and 4."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"cv = CountVectorizer(analyzer='char', ngram_range=(3,4))\n",
"\n",
"cv_nominal = cv.fit_transform(df[df['label']== 0]['uri'])\n",
"cv_all = cv.fit_transform(df['uri'])\n",
"\n",
"feature_names = cv.get_feature_names()\n",
"\n",
"import operator\n",
"sorted(cv.vocabulary_.iteritems(), key=operator.itemgetter(1), reverse= True)[0:5]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 13,
"text": [
"[(u'zzzd', 22745),\n",
" (u'zzz', 22744),\n",
" (u'zzyt', 22743),\n",
" (u'zzy', 22742),\n",
" (u'zzpa', 22741)]"
]
}
],
"prompt_number": 13
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"dfConcat = pd.concat([df.ix[:, 2:4], pd.DataFrame(cv_all.toarray())], join='outer', axis=1, ignore_index=False)\n",
"dfConcat.head(3)\n",
"# cv.vocabulary_\n",
"# cv_nominal.toarray()\n",
"# cv.get_feature_names()\n",
"# cv.vocabulary_"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>length</th>\n",
" <th>entropy</th>\n",
" <th>0</th>\n",
" <th>1</th>\n",
" <th>2</th>\n",
" <th>3</th>\n",
" <th>4</th>\n",
" <th>5</th>\n",
" <th>6</th>\n",
" <th>7</th>\n",
" <th>8</th>\n",
" <th>9</th>\n",
" <th>10</th>\n",
" <th>11</th>\n",
" <th>12</th>\n",
" <th>13</th>\n",
" <th>14</th>\n",
" <th>15</th>\n",
" <th>16</th>\n",
" <th>17</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> 6</td>\n",
" <td> 1.918296</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 8</td>\n",
" <td> 2.750000</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td> 7</td>\n",
" <td> 2.521641</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td> 0</td>\n",
" <td>...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>3 rows \u00d7 22748 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 14,
"text": [
" length entropy 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \\\n",
"0 6 1.918296 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \n",
"1 8 2.750000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \n",
"2 7 2.521641 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \n",
"\n",
" 17 \n",
"0 0 ... \n",
"1 0 ... \n",
"2 0 ... \n",
"\n",
"[3 rows x 22748 columns]"
]
}
],
"prompt_number": 14
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"X = dfConcat.values\n",
"y = df.ix[:,1]\n",
"print X[0:3]\n",
"print ''\n",
"print y[0:3]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[[ 6. 1.91829583 0. ..., 0. 0. 0. ]\n",
" [ 8. 2.75 0. ..., 0. 0. 0. ]\n",
" [ 7. 2.52164064 0. ..., 0. 0. 0. ]]\n",
"\n",
"0 0\n",
"1 0\n",
"2 0\n",
"Name: label, dtype: int64\n"
]
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Dummy Classifier**"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Since we have a labeled dataset, we can conduct a sanity check by comparing any given selected estimator against the following rules of thumb for classfication:\n",
"\n",
"- Stratified generates random predictions by respecting the training set\u2019s class distribution\n",
"- Most_frequent always predicts the most frequent label in the training set\n",
"- Uniform generates predictions uniformly at random"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from sklearn.cross_validation import train_test_split \n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=22)"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 16
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from sklearn.dummy import DummyClassifier\n",
"\n",
"for strategy in ['stratified', 'most_frequent', 'uniform']:\n",
" clf = DummyClassifier(strategy=strategy,random_state=None)\n",
" clf.fit(X_train, y_train)\n",
" print strategy + ': ' + str(clf.score(X_test, y_test))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"stratified: 0.50049652433\n",
"most_frequent: 0.547169811321\n",
"uniform: 0.513406156902\n"
]
}
],
"prompt_number": 17
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Random Forest**\n",
"\n",
"We'll leave the parameter tuning via grid search, random search, or more <a href =\"http://www.johnmyleswhite.com/notebook/2012/07/21/automatic-hyperparameter-tuning-methods/\">advanced methods</a> to another time. For now let's see what an untuned Random Forest model yields."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from sklearn.ensemble import RandomForestClassifier\n",
"from sklearn.cross_validation import cross_val_score\n",
"rf = RandomForestClassifier(n_jobs = -1)\n",
"rf.fit(X_train, y_train)\n",
"print 'RF' + ': ' + str(rf.score(X_test, y_test))\n",
" \n",
"# scores = cross_val_score(clf, X, y, cv=2)\n",
"# print scores"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"RF: 0.636544190665\n"
]
}
],
"prompt_number": 18
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The results show that an un-tuned Random Forest classifier does better than the dummy classifier which is good considering the intentional near 50/50 split of nominal and DGA domains in our dataset.\n",
"\n",
"**Adding word-grams from an English dictionary**\n",
"\n",
"Let's utilize Unix's built in words list to create a dataframe of english words that we will then use to create character n-grams. We will then compare this result to the nominal and malicious domain names to compute how many real english n-grams exist in any given domain name. For example we'd expect a domain name like \n",
"\n",
"```shell\n",
"> cat /usr/share/dict/words > eng_words.txt\n",
"\n",
"# Check file size\n",
"> du -skh words.txt \n",
"2.4M\n",
"\n",
"#Count words in file\n",
"> wc -l eng_words.txt\n",
"235886 eng_words.txt\n",
"```"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"dfEng = pd.read_csv('eng_words.txt', names=['word'],\n",
" header=None, dtype={'word': np.str},\n",
" encoding='utf-8')\n",
"\n",
"# Convert all words to lowercase to match domain name dataframes\n",
"dfEng['word'] = dfEng['word'].map(lambda x: np.str(x).strip().lower())\n",
"dfEng['word'].drop_duplicates(inplace=True)\n",
"dfEng['word'].dropna(inplace=True)\n",
"\n",
"dfEng[10:15]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>word</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>10</th>\n",
" <td> aaronic</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td> aaronical</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td> aaronite</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td> aaronitic</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td> aaru</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>5 rows \u00d7 1 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 19,
"text": [
" word\n",
"10 aaronic\n",
"11 aaronical\n",
"12 aaronite\n",
"13 aaronitic\n",
"14 aaru\n",
"\n",
"[5 rows x 1 columns]"
]
}
],
"prompt_number": 19
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"\n",
"cvEng = CountVectorizer(analyzer='char', ngram_range=(3,4))\n",
"\n",
"cvEngfeatures = cvEng.fit_transform(dfEng['word'])\n",
"\n",
"import operator\n",
"print sorted(cvEng.vocabulary_.iteritems(), key=operator.itemgetter(1), reverse= True)[0:5]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"[(u'zzy', 65452), (u'zzwi', 65451), (u'zzw', 65450), (u'zzuo', 65449), (u'zzu', 65448)]\n"
]
}
],
"prompt_number": 20
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"cvEngfeatures"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 21,
"text": [
"<234371x65453 sparse matrix of type '<type 'numpy.int64'>'\n",
"\twith 3320438 stored elements in Compressed Sparse Column format>"
]
}
],
"prompt_number": 21
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"def engDictMatch(x):\n",
" return str(np.log10(cvEngfeatures.sum(axis=0).getA1()) * cvEng.transform([x]).T)\n",
"print 'English dictionary match: ' + str(engDictMatch('yahoo')) \n",
"print 'English dictionary match: ' + str(engDictMatch('drudgereport')) \n",
"print 'English dictionary match: ' + str(engDictMatch('32tsdgseg')) "
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"English dictionary match: [ 7.95517524]\n",
"English dictionary match: [ 44.05504246]\n",
"English dictionary match: [ 2.58433122]\n"
]
}
],
"prompt_number": 22
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's evaluate this match function using the original df (before countvectorizer and dfConcat steps):"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"df['dictMatch'] = np.log10(cvEngfeatures.sum(axis=0).getA1()) * cvEng.transform(df['uri']).T \n",
"# df['dictMatch'] = df['uri'].apply(lambda x: engDictMatch(x))\n",
"df.head(3)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>uri</th>\n",
" <th>label</th>\n",
" <th>length</th>\n",
" <th>entropy</th>\n",
" <th>dictMatch</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> google</td>\n",
" <td> 0</td>\n",
" <td> 6</td>\n",
" <td> 1.918296</td>\n",
" <td> 11.885770</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> facebook</td>\n",
" <td> 0</td>\n",
" <td> 8</td>\n",
" <td> 2.750000</td>\n",
" <td> 22.813645</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td> youtube</td>\n",
" <td> 0</td>\n",
" <td> 7</td>\n",
" <td> 2.521641</td>\n",
" <td> 16.048996</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>3 rows \u00d7 5 columns</p>\n",
"</div>"
],
"metadata": {},
"output_type": "pyout",
"prompt_number": 23,
"text": [
" uri label length entropy dictMatch\n",
"0 google 0 6 1.918296 11.885770\n",
"1 facebook 0 8 2.750000 22.813645\n",
"2 youtube 0 7 2.521641 16.048996\n",
"\n",
"[3 rows x 5 columns]"
]
}
],
"prompt_number": 23
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Second Pass**\n",
"\n",
"Let's examine what effect this additional feature has on our predictive accuracy by recreating our training and test set and rerunning classification:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"dfConcat2 = pd.concat([pd.DataFrame(df.ix[:,4]),dfConcat],join='outer', axis=1, ignore_index=False)\n",
"X = dfConcat2.values\n",
"y = df.ix[:,1]\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=None)\n",
"\n",
"rf = RandomForestClassifier(n_jobs = -1)\n",
"rf.fit(X_train, y_train)\n",
"print 'RF' + ': ' + str(rf.score(X_test, y_test))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"RF: 0.722502482622\n"
]
}
],
"prompt_number": 24
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This new feature has added a small amount of power, but we could potentially gain much more by selecting our n-grams, thresholds, and other CountVectorizer parameters more intelligently. We could also create other features based on simple pattern matching concepts such as penalizing URIs if they contain more than 4 of the same character in a row. \n",
"\n",
"```python\n",
"import re\n",
"\n",
"pattern = 'ZZZ'\n",
"n = re.findall(pattern, string)\n",
"# OR we can use str.count\n",
"```"
]
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment