Logistic Regression: Train Model In Python And Use It on Angular Front End

Written by msarica | Published 2020/07/29
Tech Story Tags: machine-learning | logistic-regression | angular | scikit-learn | data-science | machine-learning-tutorials | regression | python

TLDR Logistic Regression: Train Model In Python And Use It on Angular Front End. The classifier will run entirely on the front end application. My end goal is to make predictions on the Angular application without a backend. For this example, we will do sentiment analysis on IMDB review dataset and I will skip the model training process as that's not the goal of this article. For logistic regression, we need predict and predict_proba functions. We are done with Python and we need to switch over to Angular.via the TL;DR App

Demo for this article can be found here.
I was recently looking into how I can train a model in Python/scikit-learn and use it in another platform such as Node. I could not find a tool that works out of the box. So I tried implementing one.
I will use logistic regression. Since it's a linear model, the parameters are easy to extract & interpret. Fortunately, its math is also easy and straightforward.
My end goal is to make predictions on the Angular application without a backend. The classifier will run entirely on the front end application.
For this example, we will do sentiment analysis on IMDB review dataset and I will skip the model training process as that's not the goal of this article.
[I trained a model without paying not so much attention for the sake of the demo. Here is the notebook.] After the training, we have a vectorizer and a trained logistic regression model. We can now extract the learned parameters.
The following method will write the desired model and vector parameters to a file in json format.
import json
def save_model_params(model, vectorizer, file_name='model_params.json'):
    feature_names = vectorizer.get_feature_names()
    
    d = dict()
    d['words'] = feature_names
    d['values'] = model.coef_.tolist()
    d['intercept'] = model.intercept_.tolist()
    d['classes'] = model.classes_.tolist()
    
    with open(file_name, "w") as f:
        f.write(json.dumps(d))

save_model_params(logRegModel, vectorizer)
We can copy the generated json file to our Angular project.
And that's it! We are done with Python. Let's switch over to Angular.
Angular
To get intellisense, I created a model. This is schema of the json file that I created in the previous step.
export interface ModelParams {
  words: string[],
  values: number[][],
  intercept: number[],
  classes: number[]
}
Next, I created two classes: Vectorizer and LogisticRegression as we need corresponding functionality on the Angular side.
The purpose of the vectorizer is to create a count vector to be fed to the logistic regression model. I used CountVectorizer while training the model, hence this class will match the functionality: It will create a count vector.
import { ModelParams } from './model';

/**
 * This is a class that imitates vectorizer class in 
 * scikit-learn library.
 * It has only transform method that takes the sentence,
 * removes punctuation, tokenizes and returns a count vector.
 */
export class Vectorizer {
  private punctuationRegex = /[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/g;
  private words: string[];

  constructor(params: ModelParams){
    this.words = params.words;
  }

  transform(sentence: string): number[] {
    sentence = sentence.replace(this.punctuationRegex, '');

    const words = sentence
    .split(' ')
    .map(i=>i.toLowerCase())
    .filter(i=> i.length);

    const vector = this.words.map(word=> words.filter(ww=> ww === word).length);
    return vector;
  }
}
For logistic regression, we need predict and predict_proba functions. Of course, method names don't need to match the scikit-learn's logistic regression. I'm giving the same names for clarity.
predict_proba method will calculate the probability for each class and return an array (of probabilities).
predict_proba(vector: number[]){
    // https://realpython.com/logistic-regression-python/
    // 𝑝(𝑥₁, 𝑥₂) = 1 / (1 + exp(−𝑓(𝑥₁, 𝑥₂)))
    const b = (idx: number) => 1 / (1 + Math.exp(-this.func(vector, idx)));

    // binary classification
    if(this.classes.length <=2 ){
      const result = b(0); 
      return [1-result, result]; 
    }
...
}
// intercept + w1 * x1 + w2 * x2 + ... 
private func(vector: number[], forClass: number){
    let sum = this.intercept[forClass];

    return vector.reduce((prev, vv, idx)=>{
      const iv = vv * this.values[forClass][idx];
      return prev + iv;
    }, sum);
}
predict method uses the `predict_proba` to get the array of probabilities and returns the class that has the highest probability. We are doing a binary classification, so the result will be either 0 or 1.
predict(vector: number[]): number {
    const probabilities = this.predict_proba(vector);
    const classes = this.classes;
    
    const sorted = classes
    .reduce((prev, class_, idx)=>{
      prev.push({
        class_,
        probability: probabilities[idx]
      });
      return prev;
    }, [])
    .sort((a,b)=> b.probability - a.probability);

    return sorted[0].class_;
}
Angular Service
We are done with ML stuff. Now, it's time to write our Angular service so that we can inject in our components and make predictions.
The service is pretty simple. All it does is instantiates the Vectorizer and LogisticRegression classes with model params object that we extracted from scikit-learn.
`predict` method returns true if the model predicts positive, returns false otherwise.
// json file that we exported from python
import * as variables from './model_params.json';

@Injectable({
  providedIn: 'root'
})
export class PredictionService {
  model = variables as any as ModelParams;
  vectorizer = new Vectorizer(this.model);
  logReg = new LogisticRegression(this.model);

  predict(sentence: string): boolean {
    const vector = this.vectorizer.transform(sentence);
    return this.logReg.predict(vector) === 1;
  }
}
Component
The component part is the easiest! Just inject the service and use it. That's all.
@Component({
...
})
export class CommentComponent implements OnInit {
  comment: string = '';
  isPositive: boolean;

  constructor(
    private predictionService: PredictionService
  ) { }

  ngOnInit() {
  }

  predictIfReviewIsPositive(sentence: string){
    const isPositive = this.predictionService.predict(sentence);
    this.comment = sentence;
    this.isPositive = isPositive;
    console.log(sentence, isPositive);
  }
}
Let's test! If the model predict positive, it will put a smiling emoji next to it, frowning one if otherwise. Logs will also show whether it's predicted as positive.
Conclusion
In this fun application, we used the learned parameters in Angular to run predictions.
Scikit-learn is a nice tool to train models however, none of my applications are running in Python and I don't want to write Python just to run predictions. With this approach, I can use it in any platform including a front end application.
Link to the app.

Written by msarica | msarica.com
Published by HackerNoon on 2020/07/29