Loreto Parisi

ParisiLabs blog

Month: August 2016

NLP Sentiment Analysis made easy

There are several Machine Learning and Deep Learning tools around to make sentiment analysis i.e. in bare words, the task to evalute a positive, negative or neutral feeling expressed in a text[1].

There are cloud and cognitive services like IBM Watson Alchemy Language or Microsoft Text Analytics API or the new entry, the Google Cloud Natural Language API.

There are computation frameworks that you can run on premisis like Python NLTK, Torch, Google TensorFlow. The last one is Facebook FastText[2].

But if you want to build things from the ground up, to understand how it works, you should start with something simpler, that let you train, classify, predict and test your ground truth[3]

And this is what we are going to do.

We will first active a virtualenv to let work with local installed Python packages. Let’s assume that this will be a boiler plate where everything we need will be under a `env` folder and nowhere else:

$ pip install virtualenv
$ virtualenv env
$ source env/bin/activate

Easy, isn’t it? You can check if your python version will now run in our boilerplate:

(env) $ which python
/env/bin/python
(env) $

Ok, if you see that congrats! Your virtualenv is properly set up. We can now start installing our sentiment analysis module finally! The Empathy Machines is a standalone NLP sentiment classifier written in Python and we will try it out then since it is ready to use. From the developer’s doc we know that we need some additional packages to be installed, so we will create a text file called requirements.txt, where we are going to put all our dependencies:

(env) $ mkdir sentiment
(env) $ cd sentiment
(env) $ cat >> requirements.txt
numpy>=1.11.1
scipy>=0.18.0
nltk>=3.2.1
scikit-learn>=0.17.1
empythy==0.5.6
^C

We can now ask pip program to install all these requirements:
(env) $ pip install --upgrade -r requirements.txt
Installing collected packages: numpy, scipy, empythy
Successfully installed empythy-0.5.6 numpy-1.11.1 scipy-0.18.0

So we can now write out our python program getsentiment.py

#!/usr/bin/env python

from empythy import EmpathyMachines;

nlp_classifier = EmpathyMachines()

print "training..."
nlp_classifier.train()

print "predicting..."
text="I feel very say today";
res=nlp_classifier.predict(text)
print "Sentiment for \"%s\" is %s" % (text,res)

This code will train the EmpathyMachines classifier instance against the default corpora, and will the do the prediction on our text:

(env) $ python getsentiment.py
training...
predicting...
Sentiment for "I feel very say today" is ['negative']

Wow, that’s cool, but what about adding some more interaction like a CLI?
So let’s modify the script adding some input parameters parsing with sys.argv

#!/usr/bin/env python

import sys
from empythy import EmpathyMachines;

if len(sys.argv) <= 1:
	print "\nUsage: python getsentiment.py input_text";
	exit();

nlp_classifier = EmpathyMachines()
print "training..."
nlp_classifier.train()

print "predicting..."
text=sys.argv[1]
res=nlp_classifier.predict(text)
print "Sentiment for \"%s\" is %s" % (text,res)

so now we can do some queries to our brand new sentiment api!

(env) $ python getsentiment.py "Hey I really love this day"
training...
predicting...
Sentiment for "Hey I really love this day" is ['positive']

Funny! We will see later how to turn this program into a public api, so that we can call it as a Web Service from the Internet.
In the meanwhile, star and fork this code from Tutorials

References

[1] Sentiment Analysis/Wikipedia

[2] FacebookReasearch FastText/Github

[3] Ground Truth/Wikipedia

Downloads
Sentiment Tutorials/Github

How to bridge TVML to UIKit

So far tvOS supports two ways to make tv apps, TVML and UIKit, and there is no official mentions about how to mix up things to make a TVML (that is basically XML) User Interface with the native counter part for the app logic and I/O (like playback, streaming, iCloud persistence, etc).

So, which is the best solution to mix TVML and UIKit in a new tvOS app?

In the following I have tried a solution following code snippets adapted from Apple Forums and related questions about JavaScriptCore to ObjC/Swift binding.
This is a simple wrapper class in your Swift project.

import UIKit
import TVMLKit
@objc protocol MyJSClass: JSExport {
 func getItem(key: String) -> String ?
  func setItem(key: String, data: String)
}
class MyClass: NSObject, MyJSClass {
 func getItem(key: String) -> String ? {
  return "String value"
 }

 func setItem(key: String, data: String) {
  print("Set key:\(key) value:\(data)")
 }
}

where the delegate must conform a TVApplicationControllerDelegate:

typealias TVApplicationDelegate = AppDelegate
extension TVApplicationDelegate: TVApplicationControllerDelegate {

 func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
  let myClass: MyClass = MyClass();
  jsContext.setObject(myClass, forKeyedSubscript: "objectwrapper");
 }

 func appController(appController: TVApplicationController, didFailWithError error: NSError) {
  let title = "Error Launching Application"
  let message = error.localizedDescription
  let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert) self.appController ? .navigationController.presentViewController(alertController, animated: true, completion: {
   () - & gt;Void in
  })
 }

 func appController(appController: TVApplicationController, didStopWithOptions options: [String: AnyObject] ? ) {}

 func appController(appController: TVApplicationController, didFinishLaunchingWithOptions options: [String: AnyObject] ? ) {}
}

At this point the javascript is very simple like. Take a look at the methods with named parameters, you will need to change the javascript counter part method name:

App.onLaunch = function(options) {
 var text = objectwrapper.getItem()
  // keep an eye here, the method name it changes when you have named parameters, you need camel case for parameters:
 objectwrapper.setItemData("test", "value")
}

App.onExit = function() {
 console.log('App finished');
}

Now, supposed that you have a very complex javascript interface to export like

@protocol MXMJSProtocol
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;
- (NSString*)getVersion;
@end
@interface MXMJSObject : NSObject
@end
@implementation MXMJSObject
- (NSString*)getVersion {
return @"0.0.1";
}

you can do like

JSExportAs(boot,
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3 );

At this point in the JS Counter part you will not do the camel case:

objectwrapper.bootNetworkUser(statusChanged,networkChanged,userChanged)

but you are going to do:

objectwrapper.boot(statusChanged,networkChanged,userChanged)

Finally, look at this interface again:

- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;

The value JSValue* passed in. is a way to pass completion handlers between ObjC/Swift and JavaScriptCore. At this point in the native code you do all call with arguments:

dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *state = [NSNumber numberWithInteger:status];
[networkChanged.context[@"setTimeout"]
callWithArguments:@[networkChanged, @0, state]];
});

In my findings, I have seen that the MainThread will hang if you do not dispatch on the main thread and async. So I will call the javascript “setTimeout” call that calls the completion handler callback.

So the approach I have used here is:

– Use JSExportAs to take car of methods with named parameters and avoid to camel case javascript counterparts like callMyParam1Param2Param3
– Use JSValue as parameter to get rid of completion handlers. Use callWithArguments on the native side. Use javascript functions on the JS side;
dispatch_async for completion handlers, possibly calling a setTimeout 0-delayed in the JavaScript side, to avoid the UI to freeze.

See here for discussion.

References
Apple TV Markup Language Reference
JavaScriptCore Framework Reference
Apple tvOS

Lazy loaders properties in JavaScript

I need to lazy load some class instance variables in JavaScript. Taking inspiration from Any way to define getters for lazy variables in Javascript arrays? and from Self-references in object literal declarations I have tried this solution


function MyClass() {
  this.LazyProperty = {
    _loaded: false,
    _autoload: function() {
      console.log("MyClassLazyProperty-1 " + JSON.stringify(this));
      if (this._loaded) {
        console.log("MyClass.LazyProperty-2 " + JSON.stringify(this));
        return this;
      } else {
        setTimeout(function(self) {
          self._loaded = true;
          self.profile = {
            displayName: "Loreto Parisi"
          };
          console.log("MyClass.LazyProperty-3 " + JSON.stringify(self));
          self._autoload();
        }, 300, this);
      }
    }
  }._autoload()
  this.User = {
    username: "loretoparisi",
    _autoload: function() {
      console.log("MyClass.User-1" + JSON.stringify(this));
      return this;
    }

  }._autoload()
}

var c = new MyClass();

See here for a proof of concept.