"In this example, we will show you how to run optical character recognition (OCR) on a set of documents and analyze the resulting text with the natural language processing library [spaCy](https://spacy.io/). Running OCR on a large dataset is very computationally expensive, so using Ray for distributed processing can really speed up the analysis. Ray Datasets makes it easy to compose the different steps of the pipeline, namely the OCR and the natural language processing. Ray Datasets' actor support also allows us to be more efficient by sharing the spaCy NLP context between several datapoints.\n",
"\n",
"To make it more interesting, we will run the analysis on the [LightShot](https://www.kaggle.com/datasets/datasnaek/lightshot) dataset. It is a large publicly available OCR dataset with a wide variety of different documents, all of them screenshots of various forms. It is easy to replace that dataset with your own data and adapt the example to your own use cases!\n",
"\n",
"## Overview\n",
"\n",
"This tutorial will cover:\n",
" - Creating a Ray Dataset that represents the images in the dataset\n",
" - Running the computationally expensive OCR process on each image in the dataset in parallel\n",
" - Filtering the dataset by keeping only images that contain text\n",
" - Performing various NLP operations on the text\n",
"\n",
"## Walkthrough\n",
"\n",
"Let's start by preparing the dependencies and downloading the dataset. First we install the OCR software `tesseract` and its Python client:\n",
"\n",
"````{tabbed} macOS\n",
"```\n",
"brew install tesseract\n",
"pip install pytesseract\n",
"```\n",
"````\n",
"\n",
"````{tabbed} linux\n",
"```\n",
"sudo apt-get install tesseract-ocr\n",
"pip install pytesseract\n",
"```\n",
"````\n",
"\n",
"By default, the following example will run on a tiny dataset we provide. If you want to run it on the full dataset, we recommend to run it on a cluster since processing all the images with tesseract takes a lot of time.\n",
"\n",
"````{note}\n",
"If you want to run the example on the full [LightShot](https://www.kaggle.com/datasets/datasnaek/lightshot) dataset, you need to download the dataset and extract it. You can extract the dataset by first running `unzip archive.zip` and then `unrar x LightShot13k.rar .` and then you can upload the dataset to S3 with `aws s3 cp LightShot13k/ s3://<bucket>/<folder> --recursive`.\n",
"Let's now import Ray and initialize a local Ray cluster. If you want to run OCR at a very large scale, you should run this workload on a multi-node cluster."
"We can now use the {meth}`ray.data.read_binary_files <ray.data.read_binary_files>` function to read all the images from S3. We set the `include_paths=True` option to create a dataset of the S3 paths and image contents. We then run the {meth}`ds.map <ray.data.Dataset.map>` function on this dataset to execute the actual OCR process on each file and convert the screen shots into text. This will create a tabular dataset with columns `path` and `text`, see also [](transform_datasets_row_output_types).\n",
"\n",
"````{note}\n",
"If you want to load the data from a private bucket, you have to run\n",
"2022-07-04 14:35:53,683\tWARNING read_api.py:256 -- The number of blocks in this dataset (3) limits its parallelism to 3 concurrent tasks. This is much less than the number of available CPU slots in the cluster. Use `.repartition(n)` to increase the number of dataset blocks.\n",
"### Saving and loading the result of the OCR run\n",
"\n",
"````{note}\n",
"Saving the dataset is optional, you can also continue with the in-memory data without persisting it to storage.\n",
"````\n",
"\n",
"We can save the result of running tesseract on the dataset on disk so we can read it out later if we want to re-run the NLP analysis without needing to re-run the OCR (which is very expensive on the whole dataset). This can be done with the {meth}`write_parquet <ray.data.Dataset.write_parquet>` function:"
"2022-07-04 14:36:13,515\tWARNING read_api.py:256 -- The number of blocks in this dataset (6) limits its parallelism to 6 concurrent tasks. This is much less than the number of available CPU slots in the cluster. Use `.repartition(n)` to increase the number of dataset blocks.\n"
"### Process the extracted text data with spaCy\n",
"\n",
"This is the part where the fun begins. Depending on your task there will be different needs for post processing, for example:\n",
"- If you are scanning books or articles you might want to separate the text out into sections and paragraphs.\n",
"- If you are scanning forms, receipts or checks, you might want to extract the different items listed, as well as extra information for those items like the price, or the total amount listed on the receipt or check.\n",
"- If you are scanning legal documents, you might want to extract information like the type of document, who is mentioned in the document and more semantic information about what the document claims.\n",
"- If you are scanning medical records, you might want to extract the patient name and the treatment history.\n",
"\n",
"In our specific example, let's try to determine all the documents in the LightShot dataset that are chat protocols and extract named entities in those documents. We will extract this data with spaCy. Let's first make sure the libraries are installed:"
"It gives both the language and a confidence score for that language.\n",
"\n",
"In order to run the code on the dataset, we should use Ray Datasets' built in support for actors since the `nlp` object is not serializable and we want to avoid having to recreate it for each individual sentence. We also batch the computation with the {meth}`map_batches <ray.data.Dataset.map_batches>` function to ensure spaCy can use more efficient vectorized operations where available:"
"If you are interested in this example and want to extend it, you can do the following for the full dataset:\n",
"- go throught these results in order\n",
"- create labels on whether the text is a chat conversation and then train a model like [Huggingface Transformers](https://huggingface.co/docs/transformers/) on the data.\n",
"\n",
"Contributions that extend the example in this direction with a PR are welcome!"