Troll Hunting on the Internet
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Short, Gary
The phenomenon of anonymous cyber bullies trolling individuals, as a pack, has increased over the years. One of the things that makes this appealing for the trolls is that they feel that they can act with impunity due to the anonymity provided by many social networks. In this presentation we'll walk through the development of a theoretical application to identify these trolls, and the multiple accounts they hold, via computational lingustics, before using predictive analytics to suggest likely locations for their homes and places of work/education. By removing the anonymity of the Internet we can drive these trolls off of social media.
 Okay, let's get started because we've got quite a lot to get through. As it says up here, hopefully. Yes, it does. My name's Gary Short and this session is going to be on troll hunting. That's the session is. Does everybody agree? Yes. That's good. Excellent. That means we're both in the right place. I'm in the right room. You're in the right room. Everybody's happy. Okay. So just before we get started, all right, print advisory, there will be troll tweets put it up on the on the screen from time to time. There's one shot like the whole bunch of tweets. I would be lying if I said I read them all. Okay, so there might be some dodgy language in some of them. I'm sorry if that offends you. I say I'm sorry. I don't care much, right? But just to give you a bit of a bit of a warning. Okay. So this is me. My name is Gary Short. I'm a Microsoft MVP in C sharp. Interestingly enough, none of the code you're going to see is going to be in C sharp in this session. That's just the way it's worked out. I'm a freelance data scientist and a full stack big data engineer. So basically I do all the Hadoop stuff, pig hive, all that kind of stuff as well as data science on big data and on small data. Saying you're a data scientist is a bit like saying you're a painter. That doesn't tell me whether you paint kitchens or whether you paint portraits. So actually the three things that I specialize in are predictive analytics. Predictive analytics is what we're going to see today in the session. I'm going to be showing you a machine learning algorithm to kind of create a method for making trolls disappear in kind of the same ways we make spam disappear from emails. So we're going to be seeing some predictive analytics today. I also specialize in machine vision and computational linguistics. We're not going to see any of that stuff today. So don't worry. I know it's right after lunchtime. I know everybody's a bit snoozy. I'll fill up with nice food. And we're going to do maths, which is never great after lunch, but we're going to get nice and easy and it won't be too bad. It'll be dead simple. We're not going to look at any maths in this session that a seven year old couldn't do. It's just going to be multiplication, addition, and division. Easy stuff. Even Martin can do it. The most important thing on this slide really is the bottom. This is where to get a hold of me and my website there at Duncoding it. Or you can get me at Gary at Duncoding. Or you can get me on Twitter as well. Who's on Twitter? Everybody pretty much put your hands up. Okay, there we are. I forgot I was in Norway there for a second. My apologies. Everybody put your hands up. Everybody put your both hands in the air. All right, we can do it. Excellent. All right, now put your hands up. I didn't say wait for it. Everybody put your hands in the air. Now put your hands down if you're not on Twitter. There we go. Now you see all you people didn't have your hands up at the start. If you people had just played along in the first place, right, we wouldn't have to do the silly childish stuff. All right. Okay, so if you've got a question after the session, there's not an opportunity to ask me or you can't catch me afterwards, then please feel free to email me or tweet me or whatever. Okay. All right, so first thing we're going to do is we're going to define the problem. What is the problem we're actually looking at? And the problem here is it's kind of like the same problem as we had in the early days of email where we had spam. Okay, you can't see the actual content of the email that you want to see for all the spam nonsense that's coming in on top of that. And it's kind of drowning everything out and trolling on social networks. And today we're just going to look at Twitter, but it's the same kind of thing for any other social network. Today we're going to look at trolling. Now trolling is similar to spam in as much as it drowns out the stream of information that you want to read. Okay. But to my mind, it's a little bit more nasty. It's a little bit more insidious because it's, it's insulting. Okay. You can read some of the stuff that's up there. I won't bother reading it out because that kind of stuff is, it's really horrible. All right. So it's offensive. It's threatening. Plus it does the same thing as spam did with email was kind of drown out the stuff that you want to kind of concentrate on. All right. So that's the problem. And we want to make it go away. All right. So we're either going to be kind of pretending that we're either developers at Twitter and we've been tasked with making this whole problem go away globally, or we are providing a service for individual people who want to sign up who just want trolling to disappear from their own individual timelines. So that's the problem. Trolling is unpleasant. There's no doubt about it. Okay. It's usually done by anonymous people. And the kind of stereotypical troller is this kind of angry 14 year old boy in his bedroom kind of thing. Everybody has that picture in their heads when they think about trolling. But are trolls dangerous? Well, there's been a couple of studies that says that in general, people who exhibit trolling behavior also kind of exhibit these what you would call sort of anti societal tendencies. There's a bit of sadism there and all that kind of thing. So in general, I would say if trolls are going to be dangerous, they're probably going to be dangerous to people around about them. All right. The chances of trolls being dangerous to you as an individual person who's on the receiving end of the trolling tweets from somebody who's probably a continent at least a way. It's very, very unlikely that they're actually going to be a threat to you. All right. They're more likely to be a threat to people around about them. So in general, they're probably not dangerous. They're probably not anything to worry about, but they're just unpleasant. And we really want to make that problem go away. Okay. So that's, that really is the problem. How are we going to solve it? Okay. So we're going to solve it like this in three ways, sorry, in three stages. First thing we're going to do is we have to get a hold of the tweets, okay, in the first place. So we either have to, as I say, rather pretending we're going to be at Twitter so we can do it at source, but more than likely we're not developers at Twitter. So we have to inject whatever process we have into the kind of workflow that's between, there's the tweets that you want to read which are on the Twitter servers, and here's your Twitter client. Okay. We're going to have to inject something that stands in between those things, monitors the tweets coming from the Twitter servers, deals with the trolling problem, makes all the troll tweets disappear, and then sends them on their way to your client or whatever they were going in the first place. Okay. We're not going to deal with the last part of that. We're not going to deal with injecting it back into the process. Okay. Because that kind of part is irrelevant to what we're talking about. We only have an hour. We're going to deal with that kind of first two parts, grabbing the tweets from Twitter in the first place and then classifying them as being spam or ham. Okay. I'm going to refer to it as spam and ham for two reasons. One is that we kind of, we understand that from email, spam stuff we don't want, ham stuff we do want. And secondly, because I haven't been able to find a word for good tweets that you want. You know, there's no equivalent of the word ham for social networking. So I'd have to refer to it as trolling and the other thing that's not trolling. All right. And it gets a bit monotonous saying the other thing that's not trolling. So we're just going to refer to them as spam and ham. So that's how we're going to do it. So the first thing, let's look at some code. The first thing we're going to do is we need to hook up a process to look at the Twitter API. What we're going to do is have a look at this. And this is the easiest thing that will actually work. Okay. It's quite straightforward. This is Node.js code. Anybody here not seen this before? Put your hands up. Okay. Few people not seen Node.js code before. Okay. So it's basically just JavaScript. Okay. It isn't, none of the code that you're going to see here today is particularly complicated and I'm going to walk you through it and we're all programmers in one sort of syntax or another. So I'm sure it's not going to be any problem. Okay. So the first three lines here are basically, I'm just going to be bringing in libraries that I'm going to be using later on. Okay. So that's the first three lines. The second three lines, sorry, the second set of lines here is where I instantiate a new Twitter object. Okay. So this is down from a library that I'm going to be using. We're going to instantiate that object and here you put in your consumer key, your consumer secret, the access token key and the access token secret. I have left those blank. Okay. Because I don't know, I don't want you to know what those things are for my, not that I don't trust you guys. I mean, obviously nobody would do anything bad at all with that information if I gave it to you. If you want to find out how to get that information, all right, if you watch Troy's talk next door, I'm sure he's telling you exactly how to do that kind of thing. Okay. But I've left those blank, but that's where you would fill it in with your own information from the developer site at Twitter. And then what we're going to do here is we're going to open a stream on that and we're just going to use the sample from the statuses, which basically means we're going to get a statistically significant sample. The last time I read the documentation, it was 16%. So basically you're going to get 16% of the tweets, but in a nice statistical sample and they're going to come turning in. And all I'm going to do here is I'm going to say to the stream on you getting data, what I want you to do is to call this callback function here into which you'll pass the tweet. And all I'm going to do here is to write it out onto the console. Okay. So the simplest thing that works here for us grabbing tweets is basically just to hook up to the Twitter API and then write them straight out to the console. Okay. Praise, praise straightforward and simple stuff. So let's just have a quick look at that working. Just to prove there's no smoking mirrors. And this is where we find out if the network is up and running and I haven't fallen off the Wi-Fi. So let's just run that. Yeah, very good. Anybody here know how to drive a computer? There we go. You actually have to use the right command. Who knew? All right. So that's what we've got. All of that green horrible stuff flashing past the screen there. That's all the tweets coming down just now in JSON format. Okay. That is quite enough of that. So as I said, that's, that is the simplest thing that it will get anything, that will get anything working. So you can fuel lines of code and you can start to capture the tweets. Now Twitter in Twitter says, well, that's all very well and good, right? But, but accessing our servers like that and hammering away at them doesn't do us any good whatsoever. Okay. So we issue a bunch of best practices. There are some of them are up there. The two that you have to pay most attention to, okay, is one, you can't fall behind. Okay. So if you fall behind, if you are processing the tweets, okay, and if you start to fall behind, then Twitter will issue your warning that it'll come in the tweets and not say, no, you're not keeping up. And after a while, if you're still not keeping up and you're still not keeping up, then it'll just cut the stream off. Okay. So that's the first thing you have to keep up. And the second thing is that Twitter says, from time to time, we'll, we'll cut the stream off from our side. You know, you will lose shards, the network will be a problem, there'll be issues and stuff like that. As soon as you lose connection, we don't want you continually hammering away our servers as well. If you do that, we're going to block you. Okay. So two things we have to worry about, one, keeping up and two, not hammering away at their servers. So I'll show you some code there which does the simplest possible thing which will work. Actually, if you're going to go into production with something like this, you have to pay a bit more attention, you have to solve those two problems. Okay. So let's have a look at the code where we're adhering to best practices then. All right. And as we can see here, we're like 16 lines of code here. And then when we go to best practices, we jump down to around about 250 odd. Okay. So what we have to do up here is we have to do two things. So we have to keep up and we have to adhere to the rules when Twitter cuts us off. So how we're going to keep up is basically not to process anything on that thread that is actually pulling tweets down. We want to classify the tweets, but we don't want to do it here. Okay. Because we want to make sure that we keep up. So all we're going to do to make sure we keep up is we're going to attach ourselves to the stream. And as tweets come in, we're going to put them on a queue. That's all we're going to do here. And then we can process that queue in our own time when we're happy. Okay. So that's the first thing we're going to do. So here again, at the top, I just bring in a bunch of libraries that I'm going to use. They were the same as the last set of libraries except this one here, which is going to, which we're going to use to connect to a rabbit MQ queue. All right. So what I've done now is because we've got a whole bunch of other stuff that we need to take care of and solve that second problem with not hammering away at Twitter servers, I've got a config file, which where I keep all that stuff. So the first thing we're going to do is we're going to make sure we've got that config file. And if it exists, then we're going to read it in. If it doesn't exist, you're going to, we're going to throw, we're going to log an error. Okay. Here, we're going to read that file in. Okay. Then we're going to test to make sure that the configuration file actually holds all of the information that we need it to hold. Okay. And then if it exists and it's got all of the information that we need. Okay. The first thing we're going to do is we're going to hook up to our queue. Right. We don't want to start reading tweets until we know that our queue is set up and it's and it's working properly. So next thing we're going to do is we're going to set up our queue there. Then once we've got our queue, then we're going to set up our listener and that code is pretty much just the same as the code I showed you in the previous one. So set up the queue, then set up the listener. And then as the, as the tweets start to come in this time, what we're going to do is instead of just posting them out to the console, which is what we did before, we're just going to post them on that queue. All right. So we've solved our first problem. There's no way that we can fall behind that really because all we're doing is pulling them off the queue, pushing them on, sorry, pulling them off the stream, pushing them on the queue. Down here, it's where we start to deal with the second part of that problem where we don't want to bang away at Twitter servers. Okay. So Twitter says here are all the things you've got to do for all the different kinds of errors, right? And it's documented at that URL here at the moment. So first of all, basically it says here if there's no HTTP status code, then we're going to assume that it's a TCP IP error. Okay. And at that time, what we're going to do is we're allowed to reconnect, but we have to wait for 250 milliseconds. All right. So that's that code there. The second thing that Twitter wants us to do is for HTTP errors other than for 20, we want to start with a five second delay and then double it every attempt to a maximum of 320 seconds. Once we get to 320 seconds, we can just keep going at that rate. Okay. And that's what that code here does. Next, Twitter says for HTTP errors, which for HTTP for 20 errors, then reconnect after a minute and then double each time indefinitely. So if you get a for 20 error, your back off strategy is much, much longer. Okay. So don't, we're not going to be hammering away at it the whole time. Okay. And that's basically the main points of that. So you can see if you're going to put a Twitter listener into production, you need to do quite a lot more than just the basic code to get something done. Right. Let's pop back to our slides. So that's adhering to best passes. Anybody got any questions so far? Right. Just ask them as they go along. There'll be time for questions at the end, but it's a lot easier if you've got questions if you ask them as we're going along when I'm actually looking at the code or the actual slide. It saves us jumping backwards and forwards. Nope. Everybody happy. Excellent. All right. So that's how we adhere to Twitter's best practices. All right. So the next thing we've done, we've done there is we've caught them. So that's, that was item one on our workflow. So we've gathered the tweets and now we have to process them. So what we're going to do to process them is we're going to use a machine learning to a supervised machine learning technique called Bayesian classification. And Bayesian classification at the heart of Bayesian classification is this equation here, which was actually invented by the Reverend Bayes, right? Who I dislike quite a lot. All right. So first of all, he came up with one of the fundamental algorithms of statistics and machine learning, right? Before machines were even invented. Okay. And he didn't even have the good grace to be a mathematician or a statistician. All right. The guy was a Reverend, right? He just did this in his spare time. Right. It's not even what he did for a living. All right. So when we look at this equation, it looks kind of horrible, right? But that's just because it's got a funky kind of syntax. Okay. What the syntax says here is once you actually understand what actually says, what it says is what is the probability of event A happening given that event B has already happened? Okay. And that is given by what is the probability of us seeing evidence event B given that event A is happening, as has happened, sorry, multiplied by the probability of event A happening regardless of any evidence divided by the probability of us seeing any evidence regardless of whether event A happened or not. Okay. Now, explaining it an abstract way like that is kind of horrible. It doesn't really get you any further forward. So if we if we actually swap out A's and B's for actually a concrete example, what we're saying here is what is the probability of the tweet I'm looking at being spam given that it contains this word? All right. And that's given by what is the probability of seeing that word if the tweet we were looking at is spam times the probability of a seeing spam regardless of looking at words or not. Right. So how many of all of the tweets that we receive, how many of them are spam divided by what's the probability of a seeing that word in either spam or in in ham? Okay. So that makes it a bit clearer. But this is actually because that's not difficult enough, right? A state of scientists like to make it a little bit harder. The harder it appears to you, then the more money that we get to charge for doing it. Okay. So we like to call these things differently. This probability here at the end that we're trying to calculate, we call that the posterior probability, right? So the probability at the back end for the one of a better expression. Okay. So that is how probable the event is given the evidence. This part of the equation up here is called the likelihood. Okay. This is the likelihood function and it's how probable is the evidence given that our hypothesis is correct. So how probable is it? We'll see that word given that it is spam. This part here, this is called the prior, right? And this is this represents our prior knowledge either our prior knowledge before we start looking at evidence or our prior knowledge at the time before we start looking at more evidence. Okay. Divided by this part at the bottom, we call the marginal and the marginal is how probable the evidence is under all possible hypothesis. So there we've got the posterior, the likelihood, the prior and the marginal. Okay. Which you will immediately forget after this slide goes off the screen and you know what? It doesn't really matter. Okay. All right. That was all kind of abstract and a bit mathy and kind of boring, right? Especially after lunch, right? I can see people nodding off already. It's like, oh God, he's off on one. All right. So let's take, let's take a concrete example of that. All right. So let's imagine there's a new disease going around. Okay. Let's call it endocitis. Okay. So there's a new disease going around called endocitis. But the good news is, all right, so I'm sorry, the bad news is it affects one in one thousandth of the population. Okay. If you catch endocitis, it means you have to be a Java developer for the rest of your life. Okay. A fate we can all agree worse than death. However, the good news is there is a test for endocitis and it is 98% accurate. All right. The test is 98% accurate. And what we want to know is what's the probability of having the disease given that you test positive for endocitis? So if I tell you if you catch endocitis, you'll have to be a Java programmer for the rest of your life. I administer a test to you. Okay. It comes back as positive. I'm sorry. You have tested positive for endocitis. Who would be worried by this fact? Okay. 98% positive people worried people. Okay. Yeah. I'd be worried too if I face the possibility of having to program in Java for the rest of my days. So let's do the maths and let's see what the probability is of you actually having the disease even though the test is 98% accurate. And how do we do that? The easiest way to do that is just to build this kind of binary tree. So we start at the top with all of the population. Okay. All of the population is split classified into two classes. On the left there, those with the disease that represents one in 1000 of the population. On the right hand side, the class that do not have the disease disease and Java free. That is 999 out of 1000. It pretty much models the general programming population. Right. Most people don't want to program in Java. All right. On the left hand side there of the people who do have the disease. Okay. They will be further classified as those who test positive for the disease and those who test negative. The test is 98% positive. So 98 out of 100 people on that side will be tested positive. Two people who have the disease will test negative even though they have the disease. On the far right hand side of that diagram of the people who do not have the disease, the test will be two people out of that 100. Two people in 100 will test positive for the disease even though they don't have it. Whilst 98 out of 100 will test negative. So there we basically modeled the wordy part that I gave in the slide previously. So here is our nice little Bayesian equation. Let's start to fill that in. The first thing we want to fill in is this. What's the probability of the test being positive given that I've got the disease? Okay. And so that is basically given by the part of the diagram I've colored in red now. So we have to come down that diagram. And as we jump down the same branch, what we do is we multiply out the probabilities. If we have to jump across branches, then we add them. But for the minute, we're coming down the same branch. So we're going to multiply those two things together to get to the end there. So that's the 0.98 times the 1 in 1000. Then what we have to do is divide that by the possibility of having a positive test regardless of whether or not we have the disease. And that's actually then calculated by the branches now that I've colored in red. That's how we calculate that. So when we put those figures in and we do all the arithmetic you see, I told you right at the start, we wouldn't ask you to do anything that wasn't addition, multiplication, subtraction or division. All we've done is added, multiplied and divided up until now. And as you can see when we work it out, there's only a 5% chance of you actually having the disease. And that is because although there's only a 2% error in you testing positive when you don't have it, there are the vast majority of people. 999 in 1000 don't have the disease anyway. So that 2% error gets magnified up. So although the test is 98% accurate, if you test positive, you still don't actually have much to worry about. There's only a 5% chance of you actually having the disease. So hopefully Bayesian inferencing is a lot clearer now that we've worked through an example and that's going to form the heart of our supervised machine learning algorithm, okay, for classifying our tweets. We're going to classify our tweets in exactly the same way here as we classify populations, okay. All right. What we have to do first of all though is gather the evidence. So we said the heart of the Bayesian inferencing algorithm was we have to look at evidence and we have to change that and we have to change the probability based on the evidence that we're looking at. Well, what does this evidence come from? We have to gather that evidence. And in machine learning and data science circles, this is called feature engineering because that makes it sound much, much better than I'm just trying to work out what things actually have an impact on the end probability. No, I'm not doing that. I'm not guessing. I'm not doing trial and error. I'm doing feature engineering because I am a scientist, okay. It sounds much better. But basically what that is, what that means is we're going to get a whole bunch of information down from Twitter, okay. So in a tweet, if you look at a tweet, there's lots of information regarding who they were applying to, when they signed up, what time of day they signed up, how long they'd be on Twitter, all that kind of stuff. What we have to do is we have to filter that away completely and we have to decide what actual information we need to actually work out whether our tweet is spam or ham, okay. And sometimes actually, it's a lot harder than you imagine. Sometimes it's really easy to discount things, okay. So if we discount, if we can discount what time of day the user joined Twitter in the first place, this very first time when you joined Twitter, that probably has no bearing on the fact of whether it's going to be spam or not. In the same way, it becomes very, some of them are very easy to know that they should be part of the evidence. So that is obviously in, for a tweet, the words are going to be important, okay. So it's kind of obvious that we need to know the words. But thereafter, what other information we might need, it kind of gets a little bit, it kind of gets a little bit confusing as to whether it should be in or whether it should be out. So I'm going to give you two, I'm going to give you two examples that show why this is kind of tricky. Some of the tweets that I showed you earlier, which were obviously trolling, it's just blatantly obvious trolling. But why we need more than just the words is sometimes it's a little bit trickier, right. So I'll give you two examples from my personal experience just to kind of show this, right. So one is you may or not, you may or may not be aware or you may not be following this kind of being an independence referendum in Scotland at the tail end of last year, okay. And that whole referendum thing was quite divisive between people who wanted independence and people who weren't. And on the edges, it kind of got quite fraught, okay. People were trolling each other backwards and forwards, right. Me too. I'm not going to talk about people who sent me tweets. I'm going to talk about tweets that I sent myself, okay. So one of the things that we were promised if we voted yes was that we could have independence in 18 months, right. 18 months after yes vote, full independence, right. The vote was for no, so we stayed in the union. And then since then we've been or the SNP have been badgering away trying to get full fiscal autonomy. And one of the people, one of the people who was in favour of full fiscal autonomy said it's ridiculous to kind of have a time scale for full fiscal autonomy because it depends on so many things that it's being possible to tell exactly when we'd have it. So I tweeted this person and I said, how come a few months ago, okay, you said we could have full independence, the whole thing in 18 months and now we want full fiscal autonomy which is kind of just like a little bit of independence and you have no idea how long that's going to take, okay. I got blocked for trolling for that. And I kind of think, well, you know, I didn't really think that was trolling. I thought it was kind of fair question. On the other hand, right, there is an MP, I do beg your pardon, an ex MSP who was jailed for perjury. So he stood up in court, he told lies, he got found out, he went to jail. That is a matter of public record and it's not an argument, all right. During the campaign, he posted a tweet and he said, here is the truth that you need to know about independence to which I retweeted and added, said the convicted liar, okay. Now, my first tweet, I don't think was trolling. My second tweet, I agree that was trolling, okay. Both got blocked for trolling. But you can see that both of those tweets that I made are vastly different from the ones that I showed you earlier on which are really threatening and nasty. But the difference between those two tweets isn't very great. One was trolling, one isn't trolling, all right. So clearly we need more than just the words, all right. We kind of need to know the context that they were said in maybe, so we need to know a bit more of the history of the actual conversation and we need to know who's involved in the actual conversation and how that person kind of conducts themselves normally to know whether they're trolling or not, okay. So that's why feature engineering can be a little bit difficult. And in fact, when it comes to this kind of data science classification based on sentiment for the want of a better description, feature engineering here is the hardest part to do, okay. It's the hardest part to do. But what I'm going to do is I'm going to give you a nice easy demonstration of feature engineering just so you get the point, all right. But the speaky bit beforehand, I hope I've demonstrated to you that it's kind of, it is a complicated area, right. There are very subtle differences. So let's have a quick look at feature engineering. Feature engineering, what I'm going to do is I'm going to show you, so here's feature engineering sample. So what I've done is I've made it nice and simple, okay. So the tweets, right, very, very complicated whole bunch of information, right. So let's just distill it down to the stuff that's important, all right. The person who sent the tweet and the stuff they sent, all right. Here are some sample tweets, but just cut down to the information that we're interested in. And what we're going to do is all we really want to do is having decided that this is the important stuff, all we really need to do is to write a piece of software that pulls this information out, okay, and then puts it into a format that we can consume later on, okay. So that's what we want to do. So what we want is the feature engineering F-sharpy stuff, all right. As you see, F-sharp is a nice kind of, it's very easy to express yourself in F-sharp. That's what I'm trying to say, find it easier to express myself in F-sharp than in English, it appears, okay. So what we're doing here is we're just going to, again, the same as with the node code I showed you. First few lines just basically bring in libraries we're going to use, okay. So we want the F-sharp data library and we want to use system.io because I want to write it out onto the console for you to have a look, okay. So the first thing we're going to do is we're going to use F-sharp has this nice thing called type providers, okay, which basically give you a nice static view of different kinds of data. And one of the providers that comes with F-sharp is the JSON provider. So what I've done here is I say, hey, look, I want to use the JSON provider and by the way, the schema of the data, an example of what the data is going to look like can be found in this file here that I've already shown you, okay. And then what I'm going to say is, I'm going to say, look, given that I've told you I want JSON and given that I've showed you what the schema looks like, please load up this file here, okay. So now I get tweets and this lets me do this nice piece of syntax here where I say, well, the features I'm interested in, I can create a sequence where I say, well, for every tweet in the tweets, I want to pull out the username and the text and I want the actual words of the text, right, all split up, all right. But by using a JSON provider, what that means is I can do things like say, well, tweet.user and then I can see the things which are on the user that I'm interested in and there's the name. So I don't have to keep referring back to JSON for that to say, well, you know, it's this dictionary down here and the first one and the third one and that horrible drawdown thing. I get a nice IntelliSense static view of the data and that is a big help. So it's a big advantage in using providers in F-sharp. And then this is going to give me the username, the person who sent the text and the actual tweets themselves. So if I just launch this, load this up in the repo and then we go down here and then I can print this out so you can actually see what we get. No, don't start the narrator. I have pressed the wrong button. Stop the narrator. So what I'm going to get here is a sequence. So what we started off with was JSON syntax. What I've got here now is a nice sequence where I've got the actual person who sent it and then the actual words that are in the text and then there's the next one with the person who sent that and then the individual words in the text and then there's the third one there. I've got that in a nice sequence that I can then use later on for processing. So I've taken that JSON stuff and I've used feature engineering and I've pulled out just the bits that we're interested in. Okay? And we can throw the rest of that away. Okay, any questions so far? Excellent. That means you're all stunned into silence and snoozing after your lunch or I'm explaining it really well. I'm going to pretend it's the latter. If it's the former then keep that to yourselves. Okay. Back to the slides then. Okay. Now, remember this slide here where I said that was all very well but what we were doing here is we were looking for this and we said what's the probability of that given the evidence? And there was only one B. That didn't say BCDEFGHR. But now I'm giving you a bunch of evidence. Okay? So the question then becomes well what do I do when I've got a bunch of evidence? Alright? And what that actually means is here if you recall, this is really hard, I can only fall off of this. Here we call this the prior and the prior represented our belief in the system or what we thought happened before we started to look at the evidence. Okay? And what we're going to say is this represents our prior, this represents our prior knowledge before the next piece of evidence. Alright? So when we come to B, i.e. the first piece of evidence, the prior probability is just well what was the probability of us getting spam anyway regardless of what the actual words were. Then we're going to look at this word at a time and as we add a word at a time we're going to change that probability. The probability will either go up or down depending on whether the word is a spammy word or whether it's a hammy word. And how we update that is the posterior probability here, this posterior probability for this first piece of evidence becomes the prior, the new prior probability for the second piece of evidence. Okay? So we just continually move them around until you run out of evidence. Alright? That's how that works. So even though now we're looking at multiple pieces of evidence, we can still use exactly the same formula. We just swap the prior and the posterior around for every piece of evidence that we look at. Okay? Now, I open my bottle of water and because I'm all mic'd up, you lovely ladies and gentlemen get to listen as I gurgle water and swallow it down because that always sounds attractive. Okay, anybody got any questions about the mathy bit? Nope, awesome, right. Okay, let's crack on. Training the classifier. Okay? Training the classifier sounds dangerous. It sounds like two things. It sounds dangerous. I'm going to train one of these unruly, beastie classifier things. And it also sounds really cool from a data science point of view. So like, what are you planning to do this afternoon, Gadiel, this afternoon? Stand back, right? I'm going to train a classifier. Alright? What that actually means is, alright, all I'm going to do is to build these probability models that we're talking about here. Now, that doesn't sound so cool, does it? What are you doing? I'm building probability models, right? Okay, this is what training the classifier is. Remember I said at the start, it was a supervised machine learning algorithm. So this is what we mean by supervised. Okay, when it comes to here, training the classifier, I have to supervise that learning algorithm. And how I do the supervision is, I give it a whole bunch of spammy trolly texts and a whole bunch of good tweets. And I say, look, that's what spam looks like. And that's what ham looks like. Okay, and we build probability models based on those things. And the classifier says, cool, I've got that. Alright? Who here is old like me and remembers the days when spam wasn't a problem solved at your SMTP server provider? And you had to solve it yourself by loading in something like spam base or something like that on into outlook. Okay, who remembers those days? Right, few open basher, you do not remember those days. There's no way you were born back then, right? Okay, that's exactly the same thing. Remember you had to load in spam base or something like that? All right, and then you had to point it at a folder of junk spammy emails, and you go, this is what my junk looks like, I'm going to rephrase that. All right, this is not what my junk looks like. All right, for Americans in the audience, this is what junk email looks like. Okay, and it learned. Okay, I understand what spam looks like. Thanks very much. We're going to do exactly the same thing. Okay, with the kind of ideas that, you know, in future, when people like Twitter actually solve trolling problem at source, we won't have to do that kind of thing. But right now, we'd have to load in a piece of software that does this into our Twitter client, right, in exactly the same way as we had to load in a spam assassin type thing into our email clients back in the day. Okay, so let's have a look at training the classifier. What we have to do to do that. All right, so let's jump back to our C sharp. So what I've got here is I've got a bunch of sample that basically says this is what spam looks like. Okay, it sent from a guy called bad guy to because obviously all the bad guys named themselves bad guys because it makes, you know, finding trolls a lot easier. All right, and he sends a nasty text. Okay, and down here we've got ham. Okay, and some of the ham sent by good guys, right, that makes our life easier as well for the good guys, we just named themselves good guys, that's fine. But down here, right, we've got a bad guy who sends a good tweet as well, because troll trollers, trollers, trolls don't send trolly tweets all the time. Okay, it's only going to be some of the time. So we have to be able to solve that problem. Okay, so that is our this represents our corpus, our training data. So we're going to, we're going to make the classifier look at this. So if we look at training the classifier here, what we're going to do is we're going to look at this stuff. And we're not going to go over this bit, we've gone over the first bit, we've talked about JSON providers, we've talked about loading files. So here is our spam and ham coming in. So the first thing we're going to do is we're going to create these probability models. And you'll notice that a probability model is just a dictionary, which actually has the word and the number of times that that word has appeared. Okay, and then the probability of that being ham is just going to be the number of times that that word appears in the in the ham or spam model divided by the number of all words. Okay. So that's going to be our models, they're going to be dictionaries. So what we're going to do now is we're going to look at defining a couple of generic handlers. So we're going to, we're going to create a function here, which is going to handle senders, right, we're going to pass it one of the models, one of these probability models and a sender. And this function here is going to say, well, if that sender is not in the model, then add the sender to the model and give it a count of zero, because we've not seen it yet. And then if it is in the model or now that we've added it, increment that sender by one, because I've seen it one more time. Okay. Then here, we're going to write a generic function that will handle words for us. Again, we're going to pass in one of the models, we're going to pass in a word, and then we're going to pass in a whole bunch of that same word. So it's basically like when you do a grouping, here is the key I've grouped on, here is the values of all of the, all of the times I saw that word in the text. All right. Again, we're going to do exactly the same thing. If it's, if it's not already in the model, then add it and then add to that word the number of times it appears, i.e. the length of the sequence. Then we're going to write a genetic function, which will actually call these other two generic functions, and it's going to pass in the correct, it's going to pass in the correct models here, and it's going to pass in the senders and the words. Then what we can do here is we can start to say, well, when I'm training for spam, what I'm going to do is for every tweet that I've got in spam tweets, I want to handle the tweets by passing in the spam sender model, the spam word model, the username, which is the sender, and the actual text of the tweets. I'm going to do exactly the same thing, exactly the same process for ham, except in this case, I'm going to pass in the ham sender model and the ham word model. Then what I'm going to do is I'm going to train the classifier by training for ham, sorry, training for spam and training for ham. If I load all of that up nicely, like so, just load it into the repo and then crack down here and actually run this trainer like so. Down the bottom here, what we are going to end up with is, so here's our models, here's our probability models. You can see here is the spam word model, here's the actual words and how many times that they appear. Here's the spam sender model and the ham and spam sender and word models. You can see here, this is what our probability models have, they actually look like. That's what we're done by, that's what we're called training the classifier. Training the classifier sounds kind of cool, but really we're just building mathematical models. Let's close these down. Okay, so what we end up with is a kind of frequency distribution. It looks like that. It says, look, these words, they are going to appear more often. Basically, just to make a little bit simpler and explain it to you, on this frequency distribution, the higher the column, the higher the probability that that word will appear in a spammy text. The lower the probability, so down there on the left hand side, you see your, your is not very likely to appear in a spammy text. Right at the other end, where we've got some of the words like bitch and hate and much, they are much more likely to appear in a spammy tweet. Okay, so that's all we've done when we've trained the classifier. We've built a functional frequency distribution, a probability model that looks kind of like this. Okay, so now that we have built that, now that we've trained the trainer, we can actually go ahead and classify some tweets. Okay, so now is the time to find out if our classifier works. So let's jump back into the code here. Let's get rid of that. Let's have a look at some tweets that we want to classify. Again, I've simplified it right down. Okay, I've just pulled out the relevant, so we don't have to go fishing through all the tweets to the actual information. I've, I've cut it right down, so it's simple for you to spot. So right at the top, we've got a tweet here from bad guy one, the text definitely trolling, definitely spammy. Here, we've got another tweet from bad guy two, which is also definitely spammy. So there we've got four tweets that we want it to classify. First two, definitely spammy. Okay, then we've got a second two. The first of the second one, so the third one, actually is not a spammy tweet. Right, so that shouldn't be classified as spam, even though it came from bad guy one, it's, it's still an okay tweet. Then the, the last one, it comes from good guy one, and it is also a good tweet. So if our classifier works properly, it should classify, it should classify the first two as being spam, and the second two as being ham and okay. Of the two that it classifies as being spam, it should classify, yeah, it should classify the, the first two as being spam and the, the second one. Of the two that it classifies as being ham, the first one should be lightly, should have a less of a probability of being okay because it was sent by a bad guy. Okay, but the actual context is, is still fine, right, and the last one should be all good because it's a good guy who sent it and the text is fine. Okay, so that is, that is what we want to classify, so we expect two to be spam, two not to be. So let's run our classifier and let's see what we need to do to get that to work. Again, I'm not going to go over code that I've gone over before. We're just, we're just pulling in libraries and, and opening up and setting up a, a JSON type provider. Here what I've done is I've created again some, and generic methods, generic functions to help us along here. So this first one here takes the sum of all the values in the dictionary because remember the probability is, what is the probability of this word appearing? So it's, it's, it's the count of this word divided by the sum of all words in the model. Okay, so that's basically, we know that our model is a dictionary, so what this function does is, is it sums all of the values in the dictionary. So here we pass in a dictionary and then we take the values, we turn them into, into jurors and we sum them up and we pass that back as a, as a float, right? So that's all that's doing is just summing up the values. Then what I've got here is, is I've got another function which will get a value from a dictionary. It says here, here's the dictionary that I've passed in. I want a particular value from that dictionary. What we do here is we use the try get value, which is a function on the dictionary. It's in C sharp and in F sharp. But here I just want to demonstrate how F, how cool F sharp is. If you call this function in C sharp, you have to pass it an out parameter, which will tell you whether or not that word was there, right? And if it was, here it is, right? You have to pass in an, an out parameter that you have to check first of all, before you go on to look at the, the thing. If sharp is smart enough to realize that that's what's going to happen. And what it's going to do is it's going to actually assign that to a tuple. Okay? So it will assign that out parameter to okay. And then the actual value into V. All right? So we don't have to worry about passing out parameters and all the rest of it. All right? F sharp smart enough to do that for us. And it says, no, you're going to be expecting an out parameter. Here it is kind of thing. So the syntax is much nicer. And then what we say there is, well, if everything was fine and it was actually there, give it to me. Otherwise, just return zero. Okay? And I've got a whole bunch of these helper functions which do exactly the same thing really, and calling different, different variations of that thing. All right? But down here, what we want to do is, what we want to do is we want to classify the tweets. Okay? So we need to train that classifier first because I brought in that last file that we were looking at. What I want to do is I want to train this. So I want to find out what the probability of spam is given the text and what the probability of spam is given each of the words. All right? So just want to go through there and calculate using that training information. Okay? Right. So let's pull all that stuff into the repo. And then we'll actually do our classification. Okay? So the numbers are small here. Okay? Note that the numbers are small. That is just a function of the test data that we've got. Okay? So the probabilities are not particularly high. When you say, well, yeah, okay, so they're not particularly high. What I want you to notice is the first two that our classifier marked as spam, okay, is an order of magnitude higher than the ones that it marked as being ham. Okay? So here it says the first two, this is the probability of the first two being troll tweets. Here's the probability of the second two being troll tweets. And you can see the probability for the first two being troll, being troll tweets, okay, is an order of magnitude or two orders of magnitude greater than it not being. All right? So that is quite, that is an absolute distinct, that is a distinct difference. It's definitely saying the first two are spammy, the second two are not. Okay? And that kind of, that is absolutely right because we have to look at the data before we, before we went in and had a look at it. Okay? So once we get this probability, once we've looked at the model, and once we, you know, we can play around with it, we can go back and we can do some more training, we can go back and we can do some more feature engineering to see if we can make that more distinct if, if we didn't have such a distinct difference. Here we've got a really distinct difference. It's an order of magnitude, the order of magnitude bigger, but if it's not quite distinct as that, then what you can do is you can just say, look, I want to go back, I want to do some more feature engineering, see if I can, if I can pull them apart a bit, or I want to do some more training. Okay? We're actually done now because we have a really distinct difference. Okay? So all that we need to do now is looking at that, we can run this a number of times so we can kind of get a frequency distribution of what the difference is, and then using this we can set our threshold. Okay? So we set a threshold to say, well, if we have a threshold here, we'll just, we'll kill off all the spam tweets and we'll never see them and we'll get all of the good tweets that we want to see. And one of the things that we need to do there is actually to set, to set what that threshold would be. All right? And it's up to us to set that once we've done a bit more modeling. Okay? But then we set that threshold and the spam disappears and we get our ham, as it were. Okay? So that's that. Let's go back and have a look at a little more slides. We can also have a little fun with the trolls. Okay? That's fine. That's a nice software solution to the problem. Okay? At the minute, the software doesn't exist, right? This is just kind of theoretical, interesting kind of thing that you can present at conferences. Go look, this is the kind of thing that we can do with machine learning and bit of F sharp, we can make all that stuff disappear and it's really cool. All right? But at the minute, that software doesn't exist. So at the minute, you've got two options, right? If you're being bothered by trolls and they're really bothering you and they're really threatening. The first is you can report it to the police and right and you absolutely should do this, note on the video, right? You absolutely should do that thing. All right? However, that can be a bit of a faff, right? Because the police might come along and they might, you know, take away your computers and stuff as being part of evidence and if you use your computer to do your work, that's going to be a problem, you know, and they might lose it, they might drop it, you know, they might delete some of the data accidentally. Okay? The best case scenario is you're not going to see that computer again for a considerable time. The second thing you can do is you can have a bit of fun. All right? So what we can do is in kind of the same way as, kind of the same way as what we've been doing, right? If we grab this and let's just close these ones down. Part of the information, you know, we're talking about feature engineering, we're pulling out the features that we actually needed to do our work. Part of the stuff that comes with a tweet that we actually threw away because it didn't, it didn't really have an impact on what we were doing earlier on in the talk. Part of that information is geo coded, right? Geolocation, right? If you, if you sign up for it, you can have all of your tweets geolocated, right? And that can, you know, that appears in the tweets. So what we can do is if we grab this one, here what I've done is just kind of got a bit of sample data where we've got some here in our feature engineering, what we would do is we would have the name of the guy and the location, right? That would be our feature engineering for this one, okay? And we can do a frequency distribution. And if I just grab all of that stuff like so and push it down into the repo, we can see that we get this kind of thing here. We can create a frequency distribution that said, well, you know, this location here, it appears three times as often as any other location, right? Chances are that's going to be the guy's house. Simple as that, right? If not his house, then his place of work, depending on how productive he is, okay? So knowing that we can take this most frequently used location, okay? And we can go back here and we can say, look, put it into Google, right? Put it into the type bar in Google, right? Google will say, yes, that place is here. So I was telling you before that I was on a particular side in the independence debate, okay? There were other people on the other side, as I said, around the edges and certainly coming towards the actual vote, things got a little bit fraught on both sides. And there was a particular guy who wrote me some really nasty tweets and I thought, well, I'm not going to put up with that, but I also don't want to report it to the police because I don't want that hassle. So what I did was I went onto Twitter and I followed him, okay? And he immediately followed me back because now it's great for trolers because now he can DM you the trolls, right? And now all the stuff that he sends you is in private so he can be even nastier and even more mean, which suits me fine because what that also allows me to do is to go onto the Twitter API and now because I follow this guy, I can say, please give me all of this chap's tweets. And Twitter says, certainly, here you go, boomf, right? I put them in a file and I do a frequency distribution like I just showed you how to do and I take the first two because I'm pretty sure that the first one is going to be his house and the second one is probably going to be the place where he works. So he then DMs me and he says, if you don't stop tweeting the kind of things you're tweeting, I'm going to come down and I'm going to burn your house down. And I said, well, that's fine. I go to Google and I put in that location like so and then I scroll in and I scroll in and I scroll in and I scroll in until Google pops up Street View, okay? And then I take a screen capture of that and I DM him straight back and I say, look, don't bother coming around to my house and burn it down. I'll just come around to your house and we can sort it out like gentlemen, okay? And he unfollowed me immediately and I never heard from him again, okay? So that is one of these things. Don't try that at home, folks, okay? But you can have a little bit of fun with them, right? Because this software that we've been talking about doesn't exist at the minute, okay? Okay. So that is basically us finished just to recap. What we need to do if we want to make these troll tweets disappear is we want to make the troll tweets disappear. What we need to do is we need to capture the tweets from Twitter, okay? We need to put them on a queue, all right? Because Twitter wants us to keep up. In our capturing software, we have to take, we have to pay cognisance to the fact that Twitter has all kinds of rules for if that connection gets lost and we have to follow those rules. Once we've got those rules and they're on our queue that we've queued up, we can process those things in our own time. Next thing we have to do is we have to do some feature engineering. We look at a tweet in this case and we decide what kind of information that we're going to actually base our decision on. Once we've done that feature engineering, we need to capture a whole bunch of tweets to say based on the attributes that I've said are going to be important, here is what I think is spam, here is what I think is ham. And then we have to train that classifier. Having trained the classifier, we can then run it against all future tweets, right? And we can set that threshold and we can throw away all the troll tweets that we don't want to use. I suggest when you first start, if you want to do this at home, when you first start and classify them, I wouldn't throw away the trolly tweets. You don't have to read them if they upset you, but you can use that to reclassify. You know, you should be testing that every time to say, I thought this was a spammy tweet. Is it? Yes, it was. That's fine. Oh, I thought this one was a hammy tweet. No, it's not that spam. And you can put that into your spam collection again. And periodically you can retrain that classifier. Bearing in mind that retrain in the classifier just means rebuilding those probability models, okay? And that's basically what we talked about in the last hour. I now have two minutes, okay, if anybody's got any questions they'd like to ask. Anybody? Nope, we're all done. Excellent. Either, oh, yes, sir. Just spotted the probability that came out from the good guy and the bad guy would be saying the good guy. Yes. So the question there, the question there for just for the camera is the eagle-eyed gentleman in the audience spotted that the probability for the good guy and the bad guy was exactly the same, okay? And that is basically just, it's a function of a number of things, right? It's a function of I probably took a shorthand in the actual code so I didn't want to expand it all. And I think I only actually did the probability of the words. I kind of ignored, even though I captured it, I kind of ignored that because it's exactly the same process and I didn't want to explain it, didn't want to explain the same thing twice. It's also a function of the fact that we only had like four pieces of example data with very short tweets. And we also had very little training data, right? So it's a function of all of those things. Actually, although they appeared to be exactly the same, if you looked maybe down as far as the ninth or tenth decimal place, they probably had a slight change, right? It's just that wasn't reflected. So that's why that is, right? In a big system when you're doing this properly and you've got lots of data in your training data and you've got lots of data coming in, then it will be much, much clearer. But the thing to remember is the difference between the stuff that it said there's a probability that it's a spam and the probability that this is not spam, there was an order of magnitude difference between the two things even though they were quite small. Yeah. Anybody else? Yes, sir? Yes. Do you know if there was a kind of a question that you're trying to answer, that you're trying to answer, that you're trying to answer, that you're trying to answer, there used to be something like a hard book to read. Sure. Okay. So the question there for, again for the camera is, do I know if there's a.NET version of something to wrap around the Twitter API? And there absolutely is. In fact, there are numerous, numerous. If you go to New Get and you type in Twitter, there's a bunch of them. Okay? Pick whichever one. Yes, Phil? And we use the type provider, Phil. And if you hadn't sneaked in late with the tail between your legs, you would have known that. But I did all of my code in F-sharp and I think the gentleman wants to use C-sharp. So obviously type providers. F-sharp. Okay, cool. Well, in that case, then just do it the way I did it. Okay. Anybody? Anymore for any more? Okay. In that case, thanks very much for coming along and I hope you all learned something. And thanks very much for listening.
 Okay, let's get started because we've got quite a lot to get through. As it says up  here, hopefully. Yes, it does. My name's Gary Short and this session is going to be  on troll hunting. That's the session is. Does everybody agree? Yes. That's good. Excellent.  That means we're both in the right place. I'm in the right room. You're in the right  room. Everybody's happy. Okay. So just before we get started, all right, print advisory,  there will be troll tweets put it up on the on the screen from time to time. There's one  shot like the whole bunch of tweets. I would be lying if I said I read them all. Okay,  so there might be some dodgy language in some of them. I'm sorry if that offends you. I  say I'm sorry. I don't care much, right? But just to give you a bit of a bit of a warning.  Okay. So this is me. My name is Gary Short. I'm a Microsoft MVP in C sharp. Interestingly  enough, none of the code you're going to see is going to be in C sharp in this session.  That's just the way it's worked out. I'm a freelance data scientist and a full stack  big data engineer. So basically I do all the Hadoop stuff, pig hive, all that kind of stuff  as well as data science on big data and on small data. Saying you're a data scientist  is a bit like saying you're a painter. That doesn't tell me whether you paint kitchens  or whether you paint portraits. So actually the three things that I specialize in are  predictive analytics. Predictive analytics is what we're going to see today in the session.  I'm going to be showing you a machine learning algorithm to kind of create a method for making  trolls disappear in kind of the same ways we make spam disappear from emails. So we're  going to be seeing some predictive analytics today. I also specialize in machine vision  and computational linguistics. We're not going to see any of that stuff today. So don't  worry. I know it's right after lunchtime. I know everybody's a bit snoozy. I'll fill  up with nice food. And we're going to do maths, which is never great after lunch, but we're  going to get nice and easy and it won't be too bad. It'll be dead simple. We're not going  to look at any maths in this session that a seven year old couldn't do. It's just going  to be multiplication, addition, and division. Easy stuff. Even Martin can do it. The most  important thing on this slide really is the bottom. This is where to get a hold of me  and my website there at Duncoding it. Or you can get me at Gary at Duncoding. Or you  can get me on Twitter as well. Who's on Twitter? Everybody pretty much put your hands up. Okay,  there we are. I forgot I was in Norway there for a second. My apologies. Everybody put  your hands up. Everybody put your both hands in the air. All right, we can do it. Excellent.  All right, now put your hands up. I didn't say wait for it. Everybody put your hands  in the air. Now put your hands down if you're not on Twitter. There we go. Now you see all  you people didn't have your hands up at the start. If you people had just played along  in the first place, right, we wouldn't have to do the silly childish stuff. All right.  Okay, so if you've got a question after the session, there's not an opportunity to ask  me or you can't catch me afterwards, then please feel free to email me or tweet me or  whatever. Okay. All right, so first thing we're going to do is we're going to define  the problem. What is the problem we're actually looking at? And the problem here is it's  kind of like the same problem as we had in the early days of email where we had spam.  Okay, you can't see the actual content of the email that you want to see for all the  spam nonsense that's coming in on top of that. And it's kind of drowning everything out and  trolling on social networks. And today we're just going to look at Twitter, but it's the  same kind of thing for any other social network. Today we're going to look at trolling. Now  trolling is similar to spam in as much as it drowns out the stream of information that  you want to read. Okay. But to my mind, it's a little bit more nasty. It's a little bit  more insidious because it's, it's insulting. Okay. You can read some of the stuff that's  up there. I won't bother reading it out because that kind of stuff is, it's really horrible.  All right. So it's offensive. It's threatening. Plus it does the same thing as spam did with  email was kind of drown out the stuff that you want to kind of concentrate on. All right.  So that's the problem. And we want to make it go away. All right. So we're either going  to be kind of pretending that we're either developers at Twitter and we've been tasked  with making this whole problem go away globally, or we are providing a service for individual  people who want to sign up who just want trolling to disappear from their own individual timelines.  So that's the problem.  Trolling is unpleasant. There's no doubt about it. Okay. It's usually done by anonymous people.  And the kind of stereotypical troller is this kind of angry 14 year old boy in his bedroom  kind of thing. Everybody has that picture in their heads when they think about trolling.  But are trolls dangerous? Well, there's been a couple of studies that says that in general,  people who exhibit trolling behavior also kind of exhibit these what you would call sort  of anti societal tendencies. There's a bit of sadism there and all that kind of thing.  So in general, I would say if trolls are going to be dangerous, they're probably going to  be dangerous to people around about them. All right. The chances of trolls being dangerous  to you as an individual person who's on the receiving end of the trolling tweets from  somebody who's probably a continent at least a way. It's very, very unlikely that they're  actually going to be a threat to you. All right. They're more likely to be a threat to  people around about them. So in general, they're probably not dangerous. They're probably not  anything to worry about, but they're just unpleasant. And we really want to make that  problem go away. Okay. So that's, that really is the problem. How are we going to solve  it? Okay. So we're going to solve it like this in three ways, sorry, in three stages.  First thing we're going to do is we have to get a hold of the tweets, okay, in the first  place. So we either have to, as I say, rather pretending we're going to be at Twitter so  we can do it at source, but more than likely we're not developers at Twitter. So we have  to inject whatever process we have into the kind of workflow that's between, there's the  tweets that you want to read which are on the Twitter servers, and here's your Twitter  client. Okay. We're going to have to inject something that stands in between those things,  monitors the tweets coming from the Twitter servers, deals with the trolling problem,  makes all the troll tweets disappear, and then sends them on their way to your client  or whatever they were going in the first place. Okay. We're not going to deal with the last  part of that. We're not going to deal with injecting it back into the process. Okay.  Because that kind of part is irrelevant to what we're talking about. We only have an  hour. We're going to deal with that kind of first two parts, grabbing the tweets from  Twitter in the first place and then classifying them as being spam or ham. Okay. I'm going  to refer to it as spam and ham for two reasons. One is that we kind of, we understand that  from email, spam stuff we don't want, ham stuff we do want. And secondly, because I  haven't been able to find a word for good tweets that you want. You know, there's no  equivalent of the word ham for social networking. So I'd have to refer to it as trolling and  the other thing that's not trolling. All right. And it gets a bit monotonous saying  the other thing that's not trolling. So we're just going to refer to them as spam and ham.  So that's how we're going to do it. So the first thing, let's look at some code. The  first thing we're going to do is we need to hook up a process to look at the Twitter  API. What we're going to do is have a look at this. And this is the easiest thing that  will actually work. Okay. It's quite straightforward. This is Node.js code. Anybody here not seen  this before? Put your hands up. Okay. Few people not seen Node.js code before. Okay.  So it's basically just JavaScript. Okay. It isn't, none of the code that you're going  to see here today is particularly complicated and I'm going to walk you through it and we're  all programmers in one sort of syntax or another. So I'm sure it's not going to be any problem.  Okay. So the first three lines here are basically, I'm just going to be bringing in libraries  that I'm going to be using later on. Okay. So that's the first three lines. The second  three lines, sorry, the second set of lines here is where I instantiate a new Twitter  object. Okay. So this is down from a library that I'm going to be using. We're going to  instantiate that object and here you put in your consumer key, your consumer secret,  the access token key and the access token secret. I have left those blank. Okay. Because  I don't know, I don't want you to know what those things are for my, not that I don't  trust you guys. I mean, obviously nobody would do anything bad at all with that information  if I gave it to you. If you want to find out how to get that information, all right, if  you watch Troy's talk next door, I'm sure he's telling you exactly how to do that kind  of thing. Okay. But I've left those blank, but that's where you would fill it in with  your own information from the developer site at Twitter. And then what we're going to do  here is we're going to open a stream on that and we're just going to use the sample from  the statuses, which basically means we're going to get a statistically significant sample.  The last time I read the documentation, it was 16%. So basically you're going to get  16% of the tweets, but in a nice statistical sample and they're going to come turning in.  And all I'm going to do here is I'm going to say to the stream on you getting data, what  I want you to do is to call this callback function here into which you'll pass the tweet.  And all I'm going to do here is to write it out onto the console. Okay. So the simplest  thing that works here for us grabbing tweets is basically just to hook up to the Twitter  API and then write them straight out to the console. Okay. Praise, praise straightforward  and simple stuff. So let's just have a quick look at that working. Just to prove there's  no smoking mirrors. And this is where we find out if the network is up and running and I  haven't fallen off the Wi-Fi. So let's just run that. Yeah, very good. Anybody here know  how to drive a computer? There we go. You actually have to use the right command. Who  knew? All right. So that's what we've got. All of that green horrible stuff flashing  past the screen there. That's all the tweets coming down just now in JSON format. Okay.  That is quite enough of that. So as I said, that's, that is the simplest thing that it  will get anything, that will get anything working. So you can fuel lines of code and  you can start to capture the tweets. Now Twitter in Twitter says, well, that's all very well  and good, right? But, but accessing our servers like that and hammering away at them doesn't  do us any good whatsoever. Okay. So we issue a bunch of best practices. There are some  of them are up there. The two that you have to pay most attention to, okay, is one, you  can't fall behind. Okay. So if you fall behind, if you are processing the tweets, okay, and  if you start to fall behind, then Twitter will issue your warning that it'll come in  the tweets and not say, no, you're not keeping up. And after a while, if you're still not  keeping up and you're still not keeping up, then it'll just cut the stream off. Okay.  So that's the first thing you have to keep up. And the second thing is that Twitter says,  from time to time, we'll, we'll cut the stream off from our side. You know, you will lose  shards, the network will be a problem, there'll be issues and stuff like that. As soon as  you lose connection, we don't want you continually hammering away our servers as well. If you  do that, we're going to block you. Okay. So two things we have to worry about, one,  keeping up and two, not hammering away at their servers. So I'll show you some code  there which does the simplest possible thing which will work. Actually, if you're going  to go into production with something like this, you have to pay a bit more attention,  you have to solve those two problems. Okay. So let's have a look at the code where we're  adhering to best practices then. All right. And as we can see here, we're like 16 lines  of code here. And then when we go to best practices, we jump down to around about 250  odd. Okay. So what we have to do up here is we have to do two things. So we have to keep  up and we have to adhere to the rules when Twitter cuts us off. So how we're going to  keep up is basically not to process anything on that thread that is actually pulling tweets  down. We want to classify the tweets, but we don't want to do it here. Okay. Because  we want to make sure that we keep up. So all we're going to do to make sure we keep up  is we're going to attach ourselves to the stream. And as tweets come in, we're going  to put them on a queue. That's all we're going to do here. And then we can process that  queue in our own time when we're happy. Okay. So that's the first thing we're going to do.  So here again, at the top, I just bring in a bunch of libraries that I'm going to use.  They were the same as the last set of libraries except this one here, which is going to, which  we're going to use to connect to a rabbit MQ queue. All right. So what I've done now is  because we've got a whole bunch of other stuff that we need to take care of and solve that  second problem with not hammering away at Twitter servers, I've got a config file, which  where I keep all that stuff. So the first thing we're going to do is we're going to  make sure we've got that config file. And if it exists, then we're going to read it  in. If it doesn't exist, you're going to, we're going to throw, we're going to log an  error. Okay. Here, we're going to read that file in. Okay. Then we're going to test to  make sure that the configuration file actually holds all of the information that we need  it to hold. Okay. And then if it exists and it's got all of the information that we need.  Okay. The first thing we're going to do is we're going to hook up to our queue. Right.  We don't want to start reading tweets until we know that our queue is set up and it's  and it's working properly. So next thing we're going to do is we're going to set up our queue  there. Then once we've got our queue, then we're going to set up our listener and that  code is pretty much just the same as the code I showed you in the previous one. So set up  the queue, then set up the listener. And then as the, as the tweets start to come in this  time, what we're going to do is instead of just posting them out to the console, which  is what we did before, we're just going to post them on that queue. All right. So we've  solved our first problem. There's no way that we can fall behind that really because all  we're doing is pulling them off the queue, pushing them on, sorry, pulling them off the  stream, pushing them on the queue. Down here, it's where we start to deal with the second  part of that problem where we don't want to bang away at Twitter servers. Okay. So Twitter  says here are all the things you've got to do for all the different kinds of errors, right?  And it's documented at that URL here at the moment. So first of all, basically it says  here if there's no HTTP status code, then we're going to assume that it's a TCP IP error.  Okay. And at that time, what we're going to do is we're allowed to reconnect, but we have  to wait for 250 milliseconds. All right. So that's that code there. The second thing that  Twitter wants us to do is for HTTP errors other than for 20, we want to start with a  five second delay and then double it every attempt to a maximum of 320 seconds. Once  we get to 320 seconds, we can just keep going at that rate. Okay. And that's what that code  here does. Next, Twitter says for HTTP errors, which for HTTP for 20 errors, then reconnect  after a minute and then double each time indefinitely. So if you get a for 20 error, your back off  strategy is much, much longer. Okay. So don't, we're not going to be hammering away at it  the whole time. Okay. And that's basically the main points of that. So you can see if  you're going to put a Twitter listener into production, you need to do quite a lot more  than just the basic code to get something done. Right. Let's pop back to our slides.  So that's adhering to best passes. Anybody got any questions so far? Right. Just ask  them as they go along. There'll be time for questions at the end, but it's a lot easier  if you've got questions if you ask them as we're going along when I'm actually looking  at the code or the actual slide. It saves us jumping backwards and forwards. Nope. Everybody  happy. Excellent. All right. So that's how we adhere to Twitter's best practices. All  right. So the next thing we've done, we've done there is we've caught them. So that's,  that was item one on our workflow. So we've gathered the tweets and now we have to process  them. So what we're going to do to process them is we're going to use a machine learning  to a supervised machine learning technique called Bayesian classification. And Bayesian  classification at the heart of Bayesian classification is this equation here, which was actually  invented by the Reverend Bayes, right? Who I dislike quite a lot. All right. So first  of all, he came up with one of the fundamental algorithms of statistics and machine learning,  right? Before machines were even invented. Okay. And he didn't even have the good grace  to be a mathematician or a statistician. All right. The guy was a Reverend, right? He just  did this in his spare time. Right. It's not even what he did for a living. All right.  So when we look at this equation, it looks kind of horrible, right? But that's just  because it's got a funky kind of syntax. Okay. What the syntax says here is once you actually  understand what actually says, what it says is what is the probability of event A happening  given that event B has already happened? Okay. And that is given by what is the probability  of us seeing evidence event B given that event A is happening, as has happened, sorry, multiplied  by the probability of event A happening regardless of any evidence divided by the probability  of us seeing any evidence regardless of whether event A happened or not. Okay. Now, explaining  it an abstract way like that is kind of horrible. It doesn't really get you any further forward.  So if we if we actually swap out A's and B's for actually a concrete example, what we're  saying here is what is the probability of the tweet I'm looking at being spam given  that it contains this word? All right. And that's given by what is the probability of  seeing that word if the tweet we were looking at is spam times the probability of a seeing  spam regardless of looking at words or not. Right. So how many of all of the tweets that  we receive, how many of them are spam divided by what's the probability of a seeing that  word in either spam or in in ham? Okay. So that makes it a bit clearer. But this is actually  because that's not difficult enough, right? A state of scientists like to make it a little  bit harder. The harder it appears to you, then the more money that we get to charge for doing  it. Okay. So we like to call these things differently. This probability here at the  end that we're trying to calculate, we call that the posterior probability, right? So  the probability at the back end for the one of a better expression. Okay. So that is how  probable the event is given the evidence. This part of the equation up here is called  the likelihood. Okay. This is the likelihood function and it's how probable is the evidence  given that our hypothesis is correct. So how probable is it? We'll see that word given  that it is spam. This part here, this is called the prior, right? And this is this represents  our prior knowledge either our prior knowledge before we start looking at evidence or our  prior knowledge at the time before we start looking at more evidence. Okay. Divided by  this part at the bottom, we call the marginal and the marginal is how probable the evidence  is under all possible hypothesis. So there we've got the posterior, the likelihood, the  prior and the marginal. Okay. Which you will immediately forget after this slide goes off  the screen and you know what? It doesn't really matter. Okay. All right. That was all kind  of abstract and a bit mathy and kind of boring, right? Especially after lunch, right? I can  see people nodding off already. It's like, oh God, he's off on one. All right. So let's  take, let's take a concrete example of that. All right. So let's imagine there's a new  disease going around. Okay. Let's call it endocitis. Okay. So there's a new disease  going around called endocitis. But the good news is, all right, so I'm sorry, the bad  news is it affects one in one thousandth of the population. Okay. If you catch endocitis,  it means you have to be a Java developer for the rest of your life. Okay. A fate we can  all agree worse than death. However, the good news is there is a test for endocitis and  it is 98% accurate. All right. The test is 98% accurate. And what we want to know is  what's the probability of having the disease given that you test positive for endocitis?  So if I tell you if you catch endocitis, you'll have to be a Java programmer for the rest  of your life. I administer a test to you. Okay. It comes back as positive. I'm sorry.  You have tested positive for endocitis. Who would be worried by this fact? Okay. 98% positive  people worried people. Okay. Yeah. I'd be worried too if I face the possibility of having  to program in Java for the rest of my days. So let's do the maths and let's see what the  probability is of you actually having the disease even though the test is 98% accurate.  And how do we do that? The easiest way to do that is just to build this kind of binary  tree. So we start at the top with all of the population. Okay. All of the population is  split classified into two classes. On the left there, those with the disease that represents  one in 1000 of the population. On the right hand side, the class that do not have the  disease disease and Java free. That is 999 out of 1000. It pretty much models the general  programming population. Right. Most people don't want to program in Java. All right.  On the left hand side there of the people who do have the disease. Okay. They will be  further classified as those who test positive for the disease and those who test negative.  The test is 98% positive. So 98 out of 100 people on that side will be tested positive.  Two people who have the disease will test negative even though they have the disease.  On the far right hand side of that diagram of the people who do not have the disease,  the test will be two people out of that 100. Two people in 100 will test positive for the  disease even though they don't have it. Whilst 98 out of 100 will test negative. So there  we basically modeled the wordy part that I gave in the slide previously. So here is our  nice little Bayesian equation. Let's start to fill that in. The first thing we want to  fill in is this. What's the probability of the test being positive given that I've got  the disease? Okay. And so that is basically given by the part of the diagram I've colored  in red now. So we have to come down that diagram. And as we jump down the same branch, what  we do is we multiply out the probabilities. If we have to jump across branches, then we  add them. But for the minute, we're coming down the same branch. So we're going to multiply  those two things together to get to the end there. So that's the 0.98 times the 1 in 1000.  Then what we have to do is divide that by the possibility of having a positive test regardless  of whether or not we have the disease. And that's actually then calculated by the branches  now that I've colored in red. That's how we calculate that. So when we put those figures  in and we do all the arithmetic you see, I told you right at the start, we wouldn't ask  you to do anything that wasn't addition, multiplication, subtraction or division. All  we've done is added, multiplied and divided up until now. And as you can see when we work  it out, there's only a 5% chance of you actually having the disease. And that is because although  there's only a 2% error in you testing positive when you don't have it, there are the vast  majority of people. 999 in 1000 don't have the disease anyway. So that 2% error gets  magnified up. So although the test is 98% accurate, if you test positive, you still don't actually  have much to worry about. There's only a 5% chance of you actually having the disease.  So hopefully Bayesian inferencing is a lot clearer now that we've worked through an example  and that's going to form the heart of our supervised machine learning algorithm, okay,  for classifying our tweets. We're going to classify our tweets in exactly the same way  here as we classify populations, okay. All right. What we have to do first of all though  is gather the evidence. So we said the heart of the Bayesian inferencing algorithm was we  have to look at evidence and we have to change that and we have to change the probability  based on the evidence that we're looking at. Well, what does this evidence come from? We  have to gather that evidence. And in machine learning and data science circles, this is  called feature engineering because that makes it sound much, much better than I'm just trying  to work out what things actually have an impact on the end probability. No, I'm not doing  that. I'm not guessing. I'm not doing trial and error. I'm doing feature engineering because  I am a scientist, okay. It sounds much better. But basically what that is, what that means  is we're going to get a whole bunch of information down from Twitter, okay. So in a tweet, if  you look at a tweet, there's lots of information regarding who they were applying to, when they  signed up, what time of day they signed up, how long they'd be on Twitter, all that kind  of stuff. What we have to do is we have to filter that away completely and we have to  decide what actual information we need to actually work out whether our tweet is spam  or ham, okay. And sometimes actually, it's a lot harder than you imagine. Sometimes it's  really easy to discount things, okay. So if we discount, if we can discount what time  of day the user joined Twitter in the first place, this very first time when you joined  Twitter, that probably has no bearing on the fact of whether it's going to be spam or not.  In the same way, it becomes very, some of them are very easy to know that they should  be part of the evidence. So that is obviously in, for a tweet, the words are going to be  important, okay. So it's kind of obvious that we need to know the words. But thereafter,  what other information we might need, it kind of gets a little bit, it kind of gets a little  bit confusing as to whether it should be in or whether it should be out. So I'm going  to give you two, I'm going to give you two examples that show why this is kind of tricky.  Some of the tweets that I showed you earlier, which were obviously trolling, it's just blatantly  obvious trolling. But why we need more than just the words is sometimes it's a little  bit trickier, right. So I'll give you two examples from my personal experience just to  kind of show this, right. So one is you may or not, you may or may not be aware or you  may not be following this kind of being an independence referendum in Scotland at the  tail end of last year, okay. And that whole referendum thing was quite divisive between  people who wanted independence and people who weren't. And on the edges, it kind of got  quite fraught, okay. People were trolling each other backwards and forwards, right.  Me too. I'm not going to talk about people who sent me tweets. I'm going to talk about  tweets that I sent myself, okay. So one of the things that we were promised if we voted  yes was that we could have independence in 18 months, right. 18 months after yes vote,  full independence, right. The vote was for no, so we stayed in the union. And then since  then we've been or the SNP have been badgering away trying to get full fiscal autonomy. And  one of the people, one of the people who was in favour of full fiscal autonomy said it's  ridiculous to kind of have a time scale for full fiscal autonomy because it depends on  so many things that it's being possible to tell exactly when we'd have it. So I tweeted  this person and I said, how come a few months ago, okay, you said we could have full independence,  the whole thing in 18 months and now we want full fiscal autonomy which is kind of just  like a little bit of independence and you have no idea how long that's going to take,  okay. I got blocked for trolling for that. And I kind of think, well, you know, I didn't  really think that was trolling. I thought it was kind of fair question. On the other  hand, right, there is an MP, I do beg your pardon, an ex MSP who was jailed for perjury.  So he stood up in court, he told lies, he got found out, he went to jail. That is a  matter of public record and it's not an argument, all right. During the campaign, he posted  a tweet and he said, here is the truth that you need to know about independence to which  I retweeted and added, said the convicted liar, okay. Now, my first tweet, I don't think  was trolling. My second tweet, I agree that was trolling, okay. Both got blocked for trolling.  But you can see that both of those tweets that I made are vastly different from the  ones that I showed you earlier on which are really threatening and nasty. But the difference  between those two tweets isn't very great. One was trolling, one isn't trolling, all  right. So clearly we need more than just the words, all right. We kind of need to know  the context that they were said in maybe, so we need to know a bit more of the history  of the actual conversation and we need to know who's involved in the actual conversation  and how that person kind of conducts themselves normally to know whether they're trolling  or not, okay. So that's why feature engineering can be a little bit difficult. And in fact,  when it comes to this kind of data science classification based on sentiment for the  want of a better description, feature engineering here is the hardest part to do, okay. It's  the hardest part to do. But what I'm going to do is I'm going to give you a nice easy  demonstration of feature engineering just so you get the point, all right. But the  speaky bit beforehand, I hope I've demonstrated to you that it's kind of, it is a complicated  area, right. There are very subtle differences. So let's have a quick look at feature engineering.  Feature engineering, what I'm going to do is I'm going to show you, so here's feature  engineering sample. So what I've done is I've made it nice and simple, okay. So the tweets,  right, very, very complicated whole bunch of information, right. So let's just distill  it down to the stuff that's important, all right. The person who sent the tweet and the  stuff they sent, all right. Here are some sample tweets, but just cut down to the information  that we're interested in. And what we're going to do is all we really want to do is having  decided that this is the important stuff, all we really need to do is to write a piece  of software that pulls this information out, okay, and then puts it into a format that  we can consume later on, okay. So that's what we want to do. So what we want is the feature  engineering F-sharpy stuff, all right. As you see, F-sharp is a nice kind of, it's very  easy to express yourself in F-sharp. That's what I'm trying to say, find it easier to  express myself in F-sharp than in English, it appears, okay. So what we're doing here  is we're just going to, again, the same as with the node code I showed you. First few  lines just basically bring in libraries we're going to use, okay. So we want the F-sharp  data library and we want to use system.io because I want to write it out onto the console  for you to have a look, okay. So the first thing we're going to do is we're going to  use F-sharp has this nice thing called type providers, okay, which basically give you  a nice static view of different kinds of data. And one of the providers that comes with F-sharp  is the JSON provider. So what I've done here is I say, hey, look, I want to use the JSON  provider and by the way, the schema of the data, an example of what the data is going  to look like can be found in this file here that I've already shown you, okay. And then  what I'm going to say is, I'm going to say, look, given that I've told you I want JSON  and given that I've showed you what the schema looks like, please load up this file here,  okay. So now I get tweets and this lets me do this nice piece of syntax here where I  say, well, the features I'm interested in, I can create a sequence where I say, well,  for every tweet in the tweets, I want to pull out the username and the text and I want the  actual words of the text, right, all split up, all right. But by using a JSON provider,  what that means is I can do things like say, well, tweet.user and then I can see the things  which are on the user that I'm interested in and there's the name. So I don't have to  keep referring back to JSON for that to say, well, you know, it's this dictionary down  here and the first one and the third one and that horrible drawdown thing. I get a nice  IntelliSense static view of the data and that is a big help. So it's a big advantage in  using providers in F-sharp. And then this is going to give me the username, the person  who sent the text and the actual tweets themselves. So if I just launch this, load this up in  the repo and then we go down here and then I can print this out so you can actually see  what we get. No, don't start the narrator. I have pressed the wrong button. Stop the  narrator. So what I'm going to get here is a sequence. So what we started off with was  JSON syntax. What I've got here now is a nice sequence where I've got the actual person  who sent it and then the actual words that are in the text and then there's the next  one with the person who sent that and then the individual words in the text and then  there's the third one there. I've got that in a nice sequence that I can then use later  on for processing. So I've taken that JSON stuff and I've used feature engineering and  I've pulled out just the bits that we're interested in. Okay? And we can throw the rest of that  away. Okay, any questions so far? Excellent. That means you're all stunned into silence  and snoozing after your lunch or I'm explaining it really well. I'm going to pretend it's  the latter. If it's the former then keep that to yourselves. Okay. Back to the slides then.  Okay. Now, remember this slide here where I said that was all very well but what we  were doing here is we were looking for this and we said what's the probability of that  given the evidence? And there was only one B. That didn't say BCDEFGHR. But now I'm giving  you a bunch of evidence. Okay? So the question then becomes well what do I do when I've got  a bunch of evidence? Alright? And what that actually means is here if you recall, this  is really hard, I can only fall off of this. Here we call this the prior and the prior  represented our belief in the system or what we thought happened before we started to look  at the evidence. Okay? And what we're going to say is this represents our prior, this  represents our prior knowledge before the next piece of evidence. Alright? So when we  come to B, i.e. the first piece of evidence, the prior probability is just well what was  the probability of us getting spam anyway regardless of what the actual words were.  Then we're going to look at this word at a time and as we add a word at a time we're  going to change that probability. The probability will either go up or down depending on whether  the word is a spammy word or whether it's a hammy word. And how we update that is the  posterior probability here, this posterior probability for this first piece of evidence  becomes the prior, the new prior probability for the second piece of evidence. Okay? So  we just continually move them around until you run out of evidence. Alright? That's how  that works. So even though now we're looking at multiple pieces of evidence, we can still  use exactly the same formula. We just swap the prior and the posterior around for every  piece of evidence that we look at. Okay? Now, I open my bottle of water and because I'm  all mic'd up, you lovely ladies and gentlemen get to listen as I gurgle water and swallow  it down because that always sounds attractive. Okay, anybody got any questions about the  mathy bit? Nope, awesome, right. Okay, let's crack on. Training the classifier. Okay? Training  the classifier sounds dangerous. It sounds like two things. It sounds dangerous. I'm  going to train one of these unruly, beastie classifier things. And it also sounds really  cool from a data science point of view. So like, what are you planning to do this afternoon,  Gadiel, this afternoon? Stand back, right? I'm going to train a classifier. Alright?  What that actually means is, alright, all I'm going to do is to build these probability  models that we're talking about here. Now, that doesn't sound so cool, does it? What  are you doing? I'm building probability models, right? Okay, this is what training the classifier  is. Remember I said at the start, it was a supervised machine learning algorithm. So  this is what we mean by supervised. Okay, when it comes to here, training the classifier,  I have to supervise that learning algorithm. And how I do the supervision is, I give it  a whole bunch of spammy trolly texts and a whole bunch of good tweets. And I say, look,  that's what spam looks like. And that's what ham looks like. Okay, and we build probability  models based on those things. And the classifier says, cool, I've got that. Alright? Who here  is old like me and remembers the days when spam wasn't a problem solved at your SMTP server  provider? And you had to solve it yourself by loading in something like spam base or  something like that on into outlook. Okay, who remembers those days? Right, few open  basher, you do not remember those days. There's no way you were born back then, right? Okay,  that's exactly the same thing. Remember you had to load in spam base or something like  that? All right, and then you had to point it at a folder of junk spammy emails, and  you go, this is what my junk looks like, I'm going to rephrase that. All right, this is  not what my junk looks like. All right, for Americans in the audience, this is what junk  email looks like. Okay, and it learned. Okay, I understand what spam looks like. Thanks  very much. We're going to do exactly the same thing. Okay, with the kind of ideas that,  you know, in future, when people like Twitter actually solve trolling problem at source,  we won't have to do that kind of thing. But right now, we'd have to load in a piece of  software that does this into our Twitter client, right, in exactly the same way as we had to  load in a spam assassin type thing into our email clients back in the day. Okay, so let's  have a look at training the classifier. What we have to do to do that. All right, so let's  jump back to our C sharp. So what I've got here is I've got a bunch of sample that basically  says this is what spam looks like. Okay, it sent from a guy called bad guy to because  obviously all the bad guys named themselves bad guys because it makes, you know, finding  trolls a lot easier. All right, and he sends a nasty text. Okay, and down here we've got  ham. Okay, and some of the ham sent by good guys, right, that makes our life easier as  well for the good guys, we just named themselves good guys, that's fine. But down here, right,  we've got a bad guy who sends a good tweet as well, because troll trollers, trollers,  trolls don't send trolly tweets all the time. Okay, it's only going to be some of the time.  So we have to be able to solve that problem. Okay, so that is our this represents our corpus,  our training data. So we're going to, we're going to make the classifier look at this.  So if we look at training the classifier here, what we're going to do is we're going to look  at this stuff. And we're not going to go over this bit, we've gone over the first bit, we've  talked about JSON providers, we've talked about loading files. So here is our spam and  ham coming in. So the first thing we're going to do is we're going to create these probability  models. And you'll notice that a probability model is just a dictionary, which actually  has the word and the number of times that that word has appeared. Okay, and then the  probability of that being ham is just going to be the number of times that that word appears  in the in the ham or spam model divided by the number of all words. Okay. So that's going  to be our models, they're going to be dictionaries. So what we're going to do now is we're going  to look at defining a couple of generic handlers. So we're going to, we're going to create a  function here, which is going to handle senders, right, we're going to pass it one of the models,  one of these probability models and a sender. And this function here is going to say, well,  if that sender is not in the model, then add the sender to the model and give it a count  of zero, because we've not seen it yet. And then if it is in the model or now that we've  added it, increment that sender by one, because I've seen it one more time. Okay. Then here,  we're going to write a generic function that will handle words for us. Again, we're going  to pass in one of the models, we're going to pass in a word, and then we're going to  pass in a whole bunch of that same word. So it's basically like when you do a grouping,  here is the key I've grouped on, here is the values of all of the, all of the times I saw  that word in the text. All right. Again, we're going to do exactly the same thing. If it's,  if it's not already in the model, then add it and then add to that word the number of  times it appears, i.e. the length of the sequence. Then we're going to write a genetic function,  which will actually call these other two generic functions, and it's going to pass in the correct,  it's going to pass in the correct models here, and it's going to pass in the senders and  the words. Then what we can do here is we can start to say, well, when I'm training  for spam, what I'm going to do is for every tweet that I've got in spam tweets, I want  to handle the tweets by passing in the spam sender model, the spam word model, the username,  which is the sender, and the actual text of the tweets. I'm going to do exactly the same  thing, exactly the same process for ham, except in this case, I'm going to pass in the ham  sender model and the ham word model. Then what I'm going to do is I'm going to train  the classifier by training for ham, sorry, training for spam and training for ham. If  I load all of that up nicely, like so, just load it into the repo and then crack down  here and actually run this trainer like so. Down the bottom here, what we are going to  end up with is, so here's our models, here's our probability models. You can see here is  the spam word model, here's the actual words and how many times that they appear. Here's  the spam sender model and the ham and spam sender and word models. You can see here,  this is what our probability models have, they actually look like. That's what we're  done by, that's what we're called training the classifier. Training the classifier sounds  kind of cool, but really we're just building mathematical models. Let's close these down.  Okay, so what we end up with is a kind of frequency distribution. It looks like that.  It says, look, these words, they are going to appear more often. Basically, just to make  a little bit simpler and explain it to you, on this frequency distribution, the higher  the column, the higher the probability that that word will appear in a spammy text. The  lower the probability, so down there on the left hand side, you see your, your is not  very likely to appear in a spammy text. Right at the other end, where we've got some of  the words like bitch and hate and much, they are much more likely to appear in a spammy  tweet. Okay, so that's all we've done when we've trained the classifier. We've built  a functional frequency distribution, a probability model that looks kind of like this. Okay,  so now that we have built that, now that we've trained the trainer, we can actually go ahead  and classify some tweets. Okay, so now is the time to find out if our classifier works.  So let's jump back into the code here. Let's get rid of that. Let's have a look at some  tweets that we want to classify. Again, I've simplified it right down. Okay, I've just  pulled out the relevant, so we don't have to go fishing through all the tweets to the  actual information. I've, I've cut it right down, so it's simple for you to spot. So right  at the top, we've got a tweet here from bad guy one, the text definitely trolling, definitely  spammy. Here, we've got another tweet from bad guy two, which is also definitely spammy.  So there we've got four tweets that we want it to classify. First two, definitely spammy.  Okay, then we've got a second two. The first of the second one, so the third one, actually  is not a spammy tweet. Right, so that shouldn't be classified as spam, even though it came  from bad guy one, it's, it's still an okay tweet. Then the, the last one, it comes from  good guy one, and it is also a good tweet. So if our classifier works properly, it should  classify, it should classify the first two as being spam, and the second two as being  ham and okay. Of the two that it classifies as being spam, it should classify, yeah, it  should classify the, the first two as being spam and the, the second one. Of the two that  it classifies as being ham, the first one should be lightly, should have a less of a  probability of being okay because it was sent by a bad guy. Okay, but the actual context  is, is still fine, right, and the last one should be all good because it's a good guy  who sent it and the text is fine. Okay, so that is, that is what we want to classify,  so we expect two to be spam, two not to be. So let's run our classifier and let's see  what we need to do to get that to work. Again, I'm not going to go over code that I've gone  over before. We're just, we're just pulling in libraries and, and opening up and setting  up a, a JSON type provider. Here what I've done is I've created again some, and generic  methods, generic functions to help us along here. So this first one here takes the sum  of all the values in the dictionary because remember the probability is, what is the probability  of this word appearing? So it's, it's, it's the count of this word divided by the sum  of all words in the model. Okay, so that's basically, we know that our model is a dictionary,  so what this function does is, is it sums all of the values in the dictionary. So here  we pass in a dictionary and then we take the values, we turn them into, into jurors and  we sum them up and we pass that back as a, as a float, right? So that's all that's doing  is just summing up the values. Then what I've got here is, is I've got another function  which will get a value from a dictionary. It says here, here's the dictionary that I've  passed in. I want a particular value from that dictionary. What we do here is we use  the try get value, which is a function on the dictionary. It's in C sharp and in F sharp.  But here I just want to demonstrate how F, how cool F sharp is. If you call this function  in C sharp, you have to pass it an out parameter, which will tell you whether or not that word  was there, right? And if it was, here it is, right? You have to pass in an, an out parameter  that you have to check first of all, before you go on to look at the, the thing. If sharp  is smart enough to realize that that's what's going to happen. And what it's going to do  is it's going to actually assign that to a tuple. Okay? So it will assign that out parameter  to okay. And then the actual value into V. All right? So we don't have to worry about  passing out parameters and all the rest of it. All right? F sharp smart enough to do  that for us. And it says, no, you're going to be expecting an out parameter. Here it  is kind of thing. So the syntax is much nicer. And then what we say there is, well, if everything  was fine and it was actually there, give it to me. Otherwise, just return zero. Okay?  And I've got a whole bunch of these helper functions which do exactly the same thing  really, and calling different, different variations of that thing. All right? But down here, what  we want to do is, what we want to do is we want to classify the tweets. Okay? So we need  to train that classifier first because I brought in that last file that we were looking at.  What I want to do is I want to train this. So I want to find out what the probability  of spam is given the text and what the probability of spam is given each of the words. All right?  So just want to go through there and calculate using that training information. Okay? Right.  So let's pull all that stuff into the repo. And then we'll actually do our classification.  Okay? So the numbers are small here. Okay? Note that the numbers are small. That is just  a function of the test data that we've got. Okay? So the probabilities are not particularly  high. When you say, well, yeah, okay, so they're not particularly high. What I want  you to notice is the first two that our classifier marked as spam, okay, is an order of magnitude  higher than the ones that it marked as being ham. Okay? So here it says the first two,  this is the probability of the first two being troll tweets. Here's the probability of the  second two being troll tweets. And you can see the probability for the first two being  troll, being troll tweets, okay, is an order of magnitude or two orders of magnitude greater  than it not being. All right? So that is quite, that is an absolute distinct, that is a distinct  difference. It's definitely saying the first two are spammy, the second two are not. Okay?  And that kind of, that is absolutely right because we have to look at the data before  we, before we went in and had a look at it. Okay? So once we get this probability, once  we've looked at the model, and once we, you know, we can play around with it, we can go  back and we can do some more training, we can go back and we can do some more feature  engineering to see if we can make that more distinct if, if we didn't have such a distinct  difference. Here we've got a really distinct difference. It's an order of magnitude, the  order of magnitude bigger, but if it's not quite distinct as that, then what you can  do is you can just say, look, I want to go back, I want to do some more feature engineering,  see if I can, if I can pull them apart a bit, or I want to do some more training. Okay?  We're actually done now because we have a really distinct difference. Okay? So all that  we need to do now is looking at that, we can run this a number of times so we can kind  of get a frequency distribution of what the difference is, and then using this we can  set our threshold. Okay? So we set a threshold to say, well, if we have a threshold here,  we'll just, we'll kill off all the spam tweets and we'll never see them and we'll get all  of the good tweets that we want to see. And one of the things that we need to do there  is actually to set, to set what that threshold would be. All right? And it's up to us to  set that once we've done a bit more modeling. Okay? But then we set that threshold and the  spam disappears and we get our ham, as it were. Okay? So that's that. Let's go back  and have a look at a little more slides. We can also have a little fun with the trolls.  Okay? That's fine. That's a nice software solution to the problem. Okay? At the minute,  the software doesn't exist, right? This is just kind of theoretical, interesting kind  of thing that you can present at conferences. Go look, this is the kind of thing that we  can do with machine learning and bit of F sharp, we can make all that stuff disappear  and it's really cool. All right? But at the minute, that software doesn't exist. So at  the minute, you've got two options, right? If you're being bothered by trolls and they're  really bothering you and they're really threatening. The first is you can report it to the police  and right and you absolutely should do this, note on the video, right? You absolutely should  do that thing. All right? However, that can be a bit of a faff, right? Because the police  might come along and they might, you know, take away your computers and stuff as being  part of evidence and if you use your computer to do your work, that's going to be a problem,  you know, and they might lose it, they might drop it, you know, they might delete some  of the data accidentally. Okay? The best case scenario is you're not going to see that computer  again for a considerable time. The second thing you can do is you can have a bit of fun.  All right? So what we can do is in kind of the same way as, kind of the same way as what  we've been doing, right? If we grab this and let's just close these ones down. Part of  the information, you know, we're talking about feature engineering, we're pulling out the  features that we actually needed to do our work. Part of the stuff that comes with a  tweet that we actually threw away because it didn't, it didn't really have an impact  on what we were doing earlier on in the talk. Part of that information is geo coded, right?  Geolocation, right? If you, if you sign up for it, you can have all of your tweets geolocated,  right? And that can, you know, that appears in the tweets. So what we can do is if we  grab this one, here what I've done is just kind of got a bit of sample data where we've  got some here in our feature engineering, what we would do is we would have the name  of the guy and the location, right? That would be our feature engineering for this one, okay?  And we can do a frequency distribution. And if I just grab all of that stuff like so and  push it down into the repo, we can see that we get this kind of thing here. We can create  a frequency distribution that said, well, you know, this location here, it appears three  times as often as any other location, right? Chances are that's going to be the guy's house.  Simple as that, right? If not his house, then his place of work, depending on how productive  he is, okay? So knowing that we can take this most frequently used location, okay? And we  can go back here and we can say, look, put it into Google, right? Put it into the type  bar in Google, right? Google will say, yes, that place is here. So I was telling you before  that I was on a particular side in the independence debate, okay? There were other people on the  other side, as I said, around the edges and certainly coming towards the actual vote,  things got a little bit fraught on both sides. And there was a particular guy who wrote me  some really nasty tweets and I thought, well, I'm not going to put up with that, but I also  don't want to report it to the police because I don't want that hassle. So what I did was  I went onto Twitter and I followed him, okay? And he immediately followed me back because  now it's great for trolers because now he can DM you the trolls, right? And now all  the stuff that he sends you is in private so he can be even nastier and even more mean,  which suits me fine because what that also allows me to do is to go onto the Twitter  API and now because I follow this guy, I can say, please give me all of this chap's tweets.  And Twitter says, certainly, here you go, boomf, right? I put them in a file and I do  a frequency distribution like I just showed you how to do and I take the first two because  I'm pretty sure that the first one is going to be his house and the second one is probably  going to be the place where he works. So he then DMs me and he says, if you don't stop  tweeting the kind of things you're tweeting, I'm going to come down and I'm going to burn  your house down. And I said, well, that's fine. I go to Google and I put in that location  like so and then I scroll in and I scroll in and I scroll in and I scroll in until Google  pops up Street View, okay? And then I take a screen capture of that and I DM him straight  back and I say, look, don't bother coming around to my house and burn it down. I'll  just come around to your house and we can sort it out like gentlemen, okay? And he unfollowed  me immediately and I never heard from him again, okay? So that is one of these things.  Don't try that at home, folks, okay? But you can have a little bit of fun with them, right?  Because this software that we've been talking about doesn't exist at the minute, okay? Okay.  So that is basically us finished just to recap. What we need to do if we want to make these  troll tweets disappear is we want to make the troll tweets disappear. What we need to  do is we need to capture the tweets from Twitter, okay? We need to put them on a queue, all  right? Because Twitter wants us to keep up. In our capturing software, we have to take,  we have to pay cognisance to the fact that Twitter has all kinds of rules for if that  connection gets lost and we have to follow those rules. Once we've got those rules and  they're on our queue that we've queued up, we can process those things in our own time.  Next thing we have to do is we have to do some feature engineering. We look at a tweet in  this case and we decide what kind of information that we're going to actually base our decision  on. Once we've done that feature engineering, we need to capture a whole bunch of tweets  to say based on the attributes that I've said are going to be important, here is what I  think is spam, here is what I think is ham. And then we have to train that classifier.  Having trained the classifier, we can then run it against all future tweets, right? And  we can set that threshold and we can throw away all the troll tweets that we don't want  to use. I suggest when you first start, if you want to do this at home, when you first  start and classify them, I wouldn't throw away the trolly tweets. You don't have to  read them if they upset you, but you can use that to reclassify. You know, you should be  testing that every time to say, I thought this was a spammy tweet. Is it? Yes, it was.  That's fine. Oh, I thought this one was a hammy tweet. No, it's not that spam. And you  can put that into your spam collection again. And periodically you can retrain that classifier.  Bearing in mind that retrain in the classifier just means rebuilding those probability models,  okay? And that's basically what we talked about in the last hour.  I now have two minutes, okay, if anybody's got any questions they'd like to ask. Anybody?  Nope, we're all done. Excellent. Either, oh, yes, sir.  Just spotted the probability that came out from the good guy and the bad guy would be  saying the good guy. Yes. So the question there, the question  there for just for the camera is the eagle-eyed gentleman in the audience spotted that the  probability for the good guy and the bad guy was exactly the same, okay? And that is  basically just, it's a function of a number of things, right? It's a function of I probably  took a shorthand in the actual code so I didn't want to expand it all. And I think I only  actually did the probability of the words. I kind of ignored, even though I captured  it, I kind of ignored that because it's exactly the same process and I didn't want to explain  it, didn't want to explain the same thing twice. It's also a function of the fact that  we only had like four pieces of example data with very short tweets. And we also had very  little training data, right? So it's a function of all of those things. Actually, although  they appeared to be exactly the same, if you looked maybe down as far as the ninth or tenth  decimal place, they probably had a slight change, right? It's just that wasn't reflected.  So that's why that is, right? In a big system when you're doing this properly and you've  got lots of data in your training data and you've got lots of data coming in, then it  will be much, much clearer. But the thing to remember is the difference between the stuff  that it said there's a probability that it's a spam and the probability that this is not  spam, there was an order of magnitude difference between the two things even though they were  quite small. Yeah. Anybody else? Yes, sir?  Yes.  Do you know if there was a kind of a question that you're trying to answer, that you're  trying to answer, that you're trying to answer, that you're trying to answer, there used to  be something like a hard book to read.  Sure. Okay. So the question there for, again for the camera is, do I know if there's a .NET version of something to wrap around the Twitter API? And there absolutely is. In fact,  there are numerous, numerous. If you go to New Get and you type in Twitter, there's a  bunch of them. Okay? Pick whichever one. Yes, Phil?  And we use the type provider, Phil. And if you hadn't sneaked in late with the tail between  your legs, you would have known that. But I did all of my code in F-sharp and I think  the gentleman wants to use C-sharp. So obviously type providers.  F-sharp.  Okay, cool. Well, in that case, then just do it the way I did it. Okay. Anybody? Anymore  for any more? Okay. In that case, thanks very much for coming along and I hope you all learned  something. And thanks very much for listening.
Troll_Hunting_on_the_Internet_-_Gary_Short_0_671.png Troll_Hunting_on_the_Internet_-_Gary_Short_672_1259.png Troll_Hunting_on_the_Internet_-_Gary_Short_1260_4631.png Troll_Hunting_on_the_Internet_-_Gary_Short_4632_4763.png Troll_Hunting_on_the_Internet_-_Gary_Short_4764_6575.png Troll_Hunting_on_the_Internet_-_Gary_Short_6576_7019.png Troll_Hunting_on_the_Internet_-_Gary_Short_7020_8183.png Troll_Hunting_on_the_Internet_-_Gary_Short_8184_8327.png Troll_Hunting_on_the_Internet_-_Gary_Short_8328_10607.png Troll_Hunting_on_the_Internet_-_Gary_Short_10608_10895.png Troll_Hunting_on_the_Internet_-_Gary_Short_10896_12011.png Troll_Hunting_on_the_Internet_-_Gary_Short_12012_13151.png Troll_Hunting_on_the_Internet_-_Gary_Short_13152_13847.png Troll_Hunting_on_the_Internet_-_Gary_Short_13848_13931.png Troll_Hunting_on_the_Internet_-_Gary_Short_13932_13991.png Troll_Hunting_on_the_Internet_-_Gary_Short_13992_14267.png Troll_Hunting_on_the_Internet_-_Gary_Short_14268_14927.png Troll_Hunting_on_the_Internet_-_Gary_Short_14928_15239.png Troll_Hunting_on_the_Internet_-_Gary_Short_15240_15359.png Troll_Hunting_on_the_Internet_-_Gary_Short_15360_15491.png Troll_Hunting_on_the_Internet_-_Gary_Short_15492_17195.png Troll_Hunting_on_the_Internet_-_Gary_Short_17196_17315.png Troll_Hunting_on_the_Internet_-_Gary_Short_17316_17435.png Troll_Hunting_on_the_Internet_-_Gary_Short_17436_17567.png Troll_Hunting_on_the_Internet_-_Gary_Short_17568_18815.png Troll_Hunting_on_the_Internet_-_Gary_Short_18816_18899.png Troll_Hunting_on_the_Internet_-_Gary_Short_18900_19175.png Troll_Hunting_on_the_Internet_-_Gary_Short_19176_19223.png Troll_Hunting_on_the_Internet_-_Gary_Short_19224_19391.png Troll_Hunting_on_the_Internet_-_Gary_Short_19392_19439.png Troll_Hunting_on_the_Internet_-_Gary_Short_19440_19487.png Troll_Hunting_on_the_Internet_-_Gary_Short_19488_19559.png Troll_Hunting_on_the_Internet_-_Gary_Short_19560_19691.png Troll_Hunting_on_the_Internet_-_Gary_Short_19692_19751.png Troll_Hunting_on_the_Internet_-_Gary_Short_19752_19859.png Troll_Hunting_on_the_Internet_-_Gary_Short_19860_20123.png Troll_Hunting_on_the_Internet_-_Gary_Short_20124_20171.png Troll_Hunting_on_the_Internet_-_Gary_Short_20172_20279.png Troll_Hunting_on_the_Internet_-_Gary_Short_20280_20435.png Troll_Hunting_on_the_Internet_-_Gary_Short_20436_20663.png Troll_Hunting_on_the_Internet_-_Gary_Short_20664_20927.png Troll_Hunting_on_the_Internet_-_Gary_Short_20928_20999.png Troll_Hunting_on_the_Internet_-_Gary_Short_21000_21227.png Troll_Hunting_on_the_Internet_-_Gary_Short_21228_21275.png Troll_Hunting_on_the_Internet_-_Gary_Short_21276_21791.png Troll_Hunting_on_the_Internet_-_Gary_Short_21792_22199.png Troll_Hunting_on_the_Internet_-_Gary_Short_22200_22247.png Troll_Hunting_on_the_Internet_-_Gary_Short_22248_22919.png Troll_Hunting_on_the_Internet_-_Gary_Short_22920_23171.png Troll_Hunting_on_the_Internet_-_Gary_Short_23172_23987.png Troll_Hunting_on_the_Internet_-_Gary_Short_23988_29159.png Troll_Hunting_on_the_Internet_-_Gary_Short_29160_29507.png Troll_Hunting_on_the_Internet_-_Gary_Short_29508_31403.png Troll_Hunting_on_the_Internet_-_Gary_Short_31404_36419.png Troll_Hunting_on_the_Internet_-_Gary_Short_36420_49379.png Troll_Hunting_on_the_Internet_-_Gary_Short_49380_51215.png Troll_Hunting_on_the_Internet_-_Gary_Short_51216_54419.png Troll_Hunting_on_the_Internet_-_Gary_Short_54420_54995.png Troll_Hunting_on_the_Internet_-_Gary_Short_54996_55427.png Troll_Hunting_on_the_Internet_-_Gary_Short_55428_57839.png Troll_Hunting_on_the_Internet_-_Gary_Short_57840_57947.png Troll_Hunting_on_the_Internet_-_Gary_Short_57948_62003.png Troll_Hunting_on_the_Internet_-_Gary_Short_62004_62387.png Troll_Hunting_on_the_Internet_-_Gary_Short_62388_63359.png Troll_Hunting_on_the_Internet_-_Gary_Short_63360_63443.png Troll_Hunting_on_the_Internet_-_Gary_Short_63444_64775.png Troll_Hunting_on_the_Internet_-_Gary_Short_64776_64895.png Troll_Hunting_on_the_Internet_-_Gary_Short_64896_66023.png Troll_Hunting_on_the_Internet_-_Gary_Short_66024_72143.png Troll_Hunting_on_the_Internet_-_Gary_Short_72144_72575.png Troll_Hunting_on_the_Internet_-_Gary_Short_72576_72623.png Troll_Hunting_on_the_Internet_-_Gary_Short_72624_73079.png Troll_Hunting_on_the_Internet_-_Gary_Short_73080_76847.png Troll_Hunting_on_the_Internet_-_Gary_Short_76848_78491.png Troll_Hunting_on_the_Internet_-_Gary_Short_78492_78659.png Troll_Hunting_on_the_Internet_-_Gary_Short_78660_80027.png Troll_Hunting_on_the_Internet_-_Gary_Short_80028_80855.png Troll_Hunting_on_the_Internet_-_Gary_Short_80856_80915.png Troll_Hunting_on_the_Internet_-_Gary_Short_80916_83279.png Troll_Hunting_on_the_Internet_-_Gary_Short_83280_84035.png Troll_Hunting_on_the_Internet_-_Gary_Short_84036_90479.png Troll_Hunting_on_the_Internet_-_Gary_Short_90480_94976.png