{"id":18930155,"url":"https://github.com/nheidloff/text-classification-watson-nlp","last_synced_at":"2025-09-04T13:42:55.796Z","repository":{"id":88375292,"uuid":"571503807","full_name":"nheidloff/text-classification-watson-nlp","owner":"nheidloff","description":"Text Classification with IBM Watson NLP","archived":false,"fork":false,"pushed_at":"2022-11-30T16:13:48.000Z","size":23938,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-25T02:44:35.265Z","etag":null,"topics":["ai","containers","dl","ibm","ibm-watson","kubernetes","labeling","ml","nlp-library","watson"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nheidloff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-28T09:25:17.000Z","updated_at":"2024-07-04T18:37:14.000Z","dependencies_parsed_at":"2024-03-30T16:00:37.350Z","dependency_job_id":null,"html_url":"https://github.com/nheidloff/text-classification-watson-nlp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nheidloff/text-classification-watson-nlp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nheidloff%2Ftext-classification-watson-nlp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nheidloff%2Ftext-classification-watson-nlp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nheidloff%2Ftext-classification-watson-nlp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nheidloff%2Ftext-classification-watson-nlp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nheidloff","download_url":"https://codeload.github.com/nheidloff/text-classification-watson-nlp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nheidloff%2Ftext-classification-watson-nlp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273619022,"owners_count":25138235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","containers","dl","ibm","ibm-watson","kubernetes","labeling","ml","nlp-library","watson"],"created_at":"2024-11-08T11:36:45.022Z","updated_at":"2025-09-04T13:42:55.732Z","avatar_url":"https://github.com/nheidloff.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Text Classification with IBM Watson NLP\n\nThis repo contains a sample which classifies blog posts via IBM Watson NLP (Natual Language Understanding). The repo covers the AI lifecycle, from data preparation, over training to deployment on Kubernetes.\n\n[IBM Watson NLP](https://www.ibm.com/products/ibm-watson-natural-language-processing) (Natural Language Understanding) containers can be run locally, on-premises or Kubernetes and OpenShift clusters. Via REST and gRCP APIs AI can easily be embedded in applications. IBM Watson Studio is used for the training of the model.\n\n\n### Goal\n\nThe goal is to classify blog posts from the website [heidloff.net](http://heidloff.net) whether or not they are related to IBM's Embeddable AI portfolio. To deliver a better experience than just full text searches for keywords, the model should be smart enough to understand the context. For example, Watson Studio can be used for Watson NLP, but also covers other use cases which are not related to Embeddable AI.\n\n* Related to Watson Embed: IBM Watson NLP, Embeddable AI, Watson Libraries, Watson Containers, etc.\n* Potentially related to Watson Embed: IBM Watson Studio, REST, training, etc.\n* Typically not related: IBM Watson Assistant, Watson Disovery, TensorFlow, etc.\n\n\n### Solution Description\n\n1. [Prepare Data](#step-1-prepare-data)\n2. [Label Data](#step-2-label-data)\n3. [Train Model](#step-3-train-model)\n4. [Build Image and run Predictions](#step-4-build-image-and-run-predictions)\n5. [Deploy to Minikube](#step-5-deploy-to-minikube)\n\nIn addition to the documentation below there is a blog series about this repo.\n\n* [Introduction: Text Classification Sample for IBM Watson NLP](http://heidloff.net/article/text-classification-sample-for-ibm-watson-nlp/)\n* [Step 1: Converting XML Feeds into CSVs with Sentences](http://heidloff.net/article/converting-xml-feeds-into-csvs-with-sentences/)\n* [Step 2: Labelling Sentences for Text Classifications](http://heidloff.net/article/labelling-sentences-for-text-classifications/)\n* [Step 3: Training Text Classification Models with Watson NLP](http://heidloff.net/article/training-text-classification-models-with-watson-nlp/)\n* [Step 4: Running Predictions against custom Watson NLP Models](http://heidloff.net/article/running-predictions-against-custom-watson-nlp-models/)\n* [Step 5: Deploying custom Watson NLP Text Classification Models](http://heidloff.net/article/deploying-custom-watson-nlp-text-classification-models)\n\n\n### Step 1: Prepare Data\n\nIn the first step data is read from the blog and converted to a list of sentences for all blog posts. See the [screenshots](documentation/step1/) for a walk-through.\n\nArchitecture: \n\n\u003ckbd\u003e\u003cimg src=\"documentation/step1.jpeg\" /\u003e\u003c/kbd\u003e\n\nThe original data from the blog is located in [data/heidloffblog/input/feed-pages](data/heidloffblog/input/feed-pages). The data was read via RSS feeds 'http://heidloff.net/feed/?paged=1'.\n\nThere are 8 pages with 80 posts. The split script splits each page in 10 elements stored in [data/heidloffblog/input/training](data/heidloffblog/input/training).\n\n```\n$ cd data-prep-and-predictions\n$ npm install\n$ node splitFeedPagesInElements.js\n```\n\nThe RSS XML files are converted into one resulting [CSV file](data/heidloffblog/output/all-sentences.csv), for example:\n\n```\ndocument_id,text\npage1-elem0,Building custom IBM Watson NLP Images.\npage1-elem0,IBM Watson NLP (Natural Language Understanding) and Watson Speech containers can be run locally on-premises or Kubernetes and OpenShift clusters.\npage1-elem1,Understanding IBM Watson Containers.\npage1-elem1,Via REST and gRCP APIs AI can easily be embedded in applications.\n```\n\nTo try IBM Watson NLP, a [trial](https://www.ibm.com/account/reg/us-en/signup?formid=urx-51726) is available. The container images are stored in an IBM container registry that is accessed via an [IBM Entitlement Key](https://www.ibm.com/account/reg/signup?formid=urx-51726).\n\nRun Watson NLP with Docker locally:\n\n```\n$ docker login cp.icr.io --username cp --password \u003cyour-entitlement-key\u003e\n$ mkdir models\n$ docker run -it --rm -e ACCEPT_LICENSE=true -v `pwd`/models:/app/models cp.icr.io/cp/ai/watson-nlp_syntax_izumo_lang_en_stock:1.0.7\n$ docker build . -f containers/Dockerfile -t watson-nlp-with-syntax-model:latest\n$ docker run --rm -it \\\n  -e ACCEPT_LICENSE=true \\\n  -p 8085:8085 \\\n  -p 8080:8080 \\\n  watson-nlp-with-syntax-model\n```\n\nThe creation of the CSV file has been automated:\n\n```\n$ cd data-prep-and-predictions\n$ npm install\n$ node convert.js heidloffblog ../data/heidloffblog/\n```\n\n\u003ckbd\u003e\u003cimg src=\"documentation/step1/preparation12.png\" /\u003e\u003c/kbd\u003e\n\n\n### Step 2: Label Data\n\nIn the second step data is labeled by defining for each sentence whether or not the sentence is related to Embeddable AI. See the [screenshots](documentation/step2/) for a walk-through.\n\nArchitecture: \n\n\u003ckbd\u003e\u003cimg src=\"documentation/step2.jpeg\" /\u003e\u003c/kbd\u003e\n\nThe labelling of the data needs to be done by domain experts. You need 200-300 positive sentences and the same amount of negative sentences.\n\nThe labelling is done via the open source project from IBM, called [Label Sleuth](https://www.label-sleuth.org/). The tool provides an easy to use user interface which provides suggestions what and how to label next entries. Note that not all sentences need to be labeled, but only the important ones. De-facto this removes noise from the original news documents, for example headers, footers, generic code lines etc.\n\n\u003ckbd\u003e\u003cimg src=\"documentation/step2/labelling08.png\" /\u003e\u003c/kbd\u003e\n\nThe labelling is not done on a complete document, but on single sentences of a document. If at least one sentence is positive , the whole document counts as positive. \n\nThe output of Label Sleuth are two CSV files:\n\n* From Label Sleuth: [Default](label-sleuth/heidloffblog/25_11_2022_basic.csv) \n* From Label Sleuth: [Advanced](label-sleuth/heidloffblog/25_11_2022_advanced.csv) (uses additional data items which have been labeled by the tool, not manually)\n* [Renamed Default](label-sleuth/heidloffblog/25_11_2022_basic_renamedTrueFalse.csv)\n\nLabel Sleuth returns in the second last column 'true' or 'false'. The notebook in the next step doesn't accept this. So 'true' needs to be renamed to 'WatsonEmbed' and 'false' to 'NotWatsonEmbed'. For now this needs to be done manually.  \n\nHere is the structure of the final CSV file (the renamed version):\n\n```\nworkspace_id,category_name,document_id,dataset,text,uri,element_metadata,label,label_type\nheidloff_net,WatsonEmbed,page6_elem0,heidloff_net,TensorFlow Object Detection is a powerful technology to recognize different objects in images including their positions.,heidloff_net-page6_elem0-1,{},NotWatsonEmbed,Standard\nheidloff_net,WatsonEmbed,page1_elem6,heidloff_net,Serving Watson NLP on Kubernetes with KServe ModelMesh.,heidloff_net-page1_elem6-0,{},WatsonEmbed,Standard\n```\n\n### Step 3: Train Model\n\nIn the third step the custom classification model is trained with Watson NLP and Watson Studio. See the [screenshots](documentation/step3/) for a walk-through.\n\nArchitecture: \n\n\u003ckbd\u003e\u003cimg src=\"documentation/step3.jpeg\" /\u003e\u003c/kbd\u003e\n\nThe training is done via IBM Watson Studio. IBM partners can get a test environment on [IBM TechZone](https://techzone.ibm.com/collection/watson-nlp-text-classification#tab-1).\n\nChoose the runtime template 'DO + NLP Runtime 22.1 on Python 3.9 (4 vCPU and 16 GB RAM)' and upload the [notebook](notebooks/HeidloffBlog.ipynb). Before you run it, create a project token with editior access, delete the first cell in the notebook and add the token to the notebook by clicking on the icon at the top with the three dots.\n\nThe 'slightly modified' CSV from the previous step is available on Box and is downloaded automatically by the notebook.\n\nFor the training an ensemble model is used which combines three classification models. It computes the weighted mean of classification predictions using confidence scores. It depends on the syntax model and the GloVe and USE embeddings.\n\n* CNN\n* SVM with TF-IDF features\n* SVM with USE (Universal Sentence Encoder) features\n\nHere are the results:\n\n\u003ckbd\u003e\u003cimg src=\"documentation/step3/training14.png\" /\u003e\u003c/kbd\u003e\n\n\n### Step 4: Build Image and run Predictions\n\nIn the fourth step the custom classification model is downloaded, an image is built and test predictions are run. See the [screenshots](documentation/step4/) for a walk-through.\n\nArchitecture: \n\n\u003ckbd\u003e\u003cimg src=\"documentation/step4.jpeg\" /\u003e\u003c/kbd\u003e\n\nThe trained model is stored in the Watson Studio project and can be downloaded (1.2 GB). Download ensemble_model.zip to the directory 'models', rename it to 'ensemble_model_heidloff.zip', unzip it and delete it.\n\nRun these commands to build the image. Make sure 1. the syntax model and 2. your ensemble_model_heidloff model are in the models directory.\n\n```\n$ docker login cp.icr.io --username cp --password \u003cyour-entitlement-key\u003e\n$ chmod -R 777 models/ensemble_model_heidloff\n$ docker build . -f containers/Dockerfile -t watson-nlp-with-classification-model\n```\n\nRun the container locally via Docker:\n\n```\n$ docker run --name watson-nlp-with-classification-model --rm -it \\\n  -e ACCEPT_LICENSE=true \\\n  -p 8085:8085 \\\n  -p 8080:8080 \\\n  watson-nlp-with-classification-model\n```\n\nYou can run predictions via curl:\n\n```\n$ curl -s -X POST \"http://localhost:8080/v1/watson.runtime.nlp.v1/NlpService/ClassificationPredict\" \\\n  -H \"accept: application/json\" \\\n  -H \"grpc-metadata-mm-model-id: ensemble_model_heidloff\" \\\n  -H \"content-type: application/json\" \\\n  -d \"{ \\\"rawDocument\\\": \\\n    { \\\"text\\\": \\\"The Watson NLP containers also provides a gRCP interface\\\" }}\" | jq\n```\n\n\u003ckbd\u003e\u003cimg src=\"documentation/step4/build-image07.png\" /\u003e\u003c/kbd\u003e\n\nAlternatively you can run a Node.js app. Put xml file(s) in 'data/heidloffblog/input/testing'. There are some sample files.\n\n```\n$ cd data-prep-and-predictions\n$ npm install\n$ node predict.js heidloffblog ../data/heidloffblog/ watson-embed-containers.xml ensemble_model_heidloff\nArticle IS about WatsonEmbed since the confidence level of one sentence is above 0.8\nThe sentence with the highest confidence is:\nIBM Watson NLP (Natural Language Understanding) and Watson Speech containers can be run locally, on-premises or Kubernetes and OpenShift clusters.\n{ className: 'WatsonEmbed', confidence: 0.9859272 }\n$ node predict.js heidloffblog ../data/heidloffblog/ not-watson-embed-studio.xml ensemble_model_heidloff\nArticle IS NOT about WatsonEmbed since the confidence level of no sentence is above 0.8\nThe sentence with the highest confidence is:\nFor example IBM provides Watson Machine Learning [http://heidloff.net/article/watson-machine-learning-sample] to identify the best algorithm.\n{ className: 'WatsonEmbed', confidence: 0.6762146 }\n```\n\n\u003ckbd\u003e\u003cimg src=\"documentation/step4/prediction01.png\" /\u003e\u003c/kbd\u003e\n\n\n### Step 5: Deploy to Minikube\n\nIn the fifth step a custom image for the custom classification model is built and deployed together with the Watson NLP runtime to Minikube. See the [screenshots](documentation/step5/) for a walk-through.\n\nArchitecture: \n\n\u003ckbd\u003e\u003cimg src=\"documentation/step5.jpeg\" /\u003e\u003c/kbd\u003e\n\nThe following commands show how to deploy to Minikube, but can easily be adopted to other Kubernetes platforms.\n\nIn contrast to above, the custom model is stored in its own image (init container). Make sure only your one model is in your 'models' directory.\n\n```\n$ tree models\nmodels\n└── ensemble_model_heidloff\n    ├── cnn_model\n    │   ├── ...\n    ├── ...\n    ├── ensemble_model\n    └── use_svm_model\n```\n\nThere are two ways to create the image.\n\n1. Via watson-embed-model-packager tool\n2. Via Dockerfile\n\nIn the snippets below the Minikube image registry is used directly since 1. I ran into issues pulling the large images and 2. it's faster for development. Make sure you give Minikube enough resources.\n\n```\n$ brew install minikube \n$ minikube start --cpus 12 --memory 16000 --disk-size 50g\n$ eval $(minikube -p minikube docker-env)\n```\n\n*watson-embed-model-packager tool*\n\n```\n$ python3 -m venv client-env\n$ source client-env/bin/activate\n$ pip install watson-embed-model-packager\n$ python3 -m watson_embed_model_packager setup --library-version watson_nlp:3.2.0 --local-model-dir models --output-csv ./containers/heidloffblog/heidloffblog.csv\n$ python3 -m watson_embed_model_packager build --config ./containers/heidloffblog/heidloffblog.csv\n$ docker tag watson-nlp_ensemble_model_heidloff virtual/watson-nlp_ensemble_model_heidloff:0.0.1\n```\n\n*Dockerfile*\n\n```\n$ WATSON_EMBED_DEMOS=./containers/build-model-image\n$ chmod 777 ${WATSON_EMBED_DEMOS}/copy_bin_with_libs.sh     \n$ chmod 777 ${WATSON_EMBED_DEMOS}/unpack_model.sh \n$ docker build -f ${WATSON_EMBED_DEMOS}/Dockerfile --build-arg MODEL_PATH=./models --build-arg MODEL_DEST=/tmp/models -t watson-nlp_ensemble_model_heidloff .\n$ docker tag watson-nlp_ensemble_model_heidloff virtual/watson-nlp_ensemble_model_heidloff:0.0.1\n```\n\nPull Watson NLP images:\n\n```\n$ docker login cp.icr.io --username cp --password \u003centitlement_key\u003e \n$ docker pull cp.icr.io/cp/ai/watson-nlp-runtime:1.0.18\n$ docker pull cp.icr.io/cp/ai/watson-nlp_syntax_izumo_lang_en_stock:1.0.7\n$ docker images | grep watson-nlp\nvirtual/watson-nlp_ensemble_model_heidloff              0.0.1     \ncp.icr.io/cp/ai/watson-nlp-runtime                      1.0.18    \ncp.icr.io/cp/ai/watson-nlp_syntax_izumo_lang_en_stock   1.0.7   \n```\n\nDeploy Watson NLP pod via Helm chart:\n\n```\n$ kubectl create namespace watson-demo\n$ kubectl config set-context --current --namespace=watson-demo\n$ kubectl create secret docker-registry \\\n--docker-server=cp.icr.io \\\n--docker-username=cp \\\n--docker-password=\u003cyour IBM Entitlement Key\u003e \\\n-n watson-demo \\\nibm-entitlement-key\n$ git clone https://github.com/cloud-native-toolkit/terraform-gitops-watson-nlp\n$ cp ./containers/heidloffblog/values.yaml ../terraform-gitops-watson-nlp/chart/watson-nlp/values.yaml\n$ cd ../terraform-gitops-watson-nlp/chart/watson-nlp\n$ helm install -f values.yaml watson-embedded .\n$ kubectl get pods -n watson-demo --watch\n$ kubectl get deployment/watson-embedded-watson-nlp -n watson-demo\n$ kubectl get svc/watson-embedded-watson-nlp -n watson-demo\n$ kubectl port-forward svc/watson-embedded-watson-nlp 8080\n```\n\nAfter this Watson NLP can be invoked for predictions with your model:\n\n```\n$ curl -s -X POST \"http://localhost:8080/v1/watson.runtime.nlp.v1/NlpService/ClassificationPredict\" \\\n  -H \"accept: application/json\" \\\n  -H \"grpc-metadata-mm-model-id: ensemble_model_heidloff\" \\\n  -H \"content-type: application/json\" \\\n  -d \"{ \\\"rawDocument\\\": \\\n    { \\\"text\\\": \\\"The Watson NLP containers also provides a gRCP interface\\\" }}\" | jq\n```\n\n\u003ckbd\u003e\u003cimg src=\"documentation/step5/minikube01.png\" /\u003e\u003c/kbd\u003e\n\n\n### Resources\n\nTo learn more about IBM Watson NLP and Embeddable AI, check out these [resources](http://heidloff.net/article/the-ultimate-guide-to-ibm-watson-libraries/).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnheidloff%2Ftext-classification-watson-nlp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnheidloff%2Ftext-classification-watson-nlp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnheidloff%2Ftext-classification-watson-nlp/lists"}