Initial fork commits
Enable fallback on language speech pipeline Force always onnxruntime-web
@@ -1,23 +1,6 @@
|
||||
|
||||
|
||||
<p align="center">
|
||||
<br/>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/xenova/transformers.js/assets/26504141/bd047e0f-aca9-4ff7-ba07-c7ca55442bc4" width="500" style="max-width: 100%;">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://github.com/xenova/transformers.js/assets/26504141/84a5dc78-f4ea-43f4-96f2-b8c791f30a8e" width="500" style="max-width: 100%;">
|
||||
<img alt="transformers.js javascript library logo" src="https://github.com/xenova/transformers.js/assets/26504141/84a5dc78-f4ea-43f4-96f2-b8c791f30a8e" width="500" style="max-width: 100%;">
|
||||
</picture>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/@xenova/transformers"><img alt="NPM" src="https://img.shields.io/npm/v/@xenova/transformers"></a>
|
||||
<a href="https://www.npmjs.com/package/@xenova/transformers"><img alt="NPM Downloads" src="https://img.shields.io/npm/dw/@xenova/transformers"></a>
|
||||
<a href="https://www.jsdelivr.com/package/npm/@xenova/transformers"><img alt="jsDelivr Hits" src="https://img.shields.io/jsdelivr/npm/hw/@xenova/transformers"></a>
|
||||
<a href="https://github.com/xenova/transformers.js/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/xenova/transformers.js?color=blue"></a>
|
||||
<a href="https://huggingface.co/docs/transformers.js/index"><img alt="Documentation" src="https://img.shields.io/website/http/huggingface.co/docs/transformers.js/index.svg?down_color=red&down_message=offline&up_message=online"></a>
|
||||
</p>
|
||||
|
||||
> [!NOTE]
|
||||
> This is a forked version of [Transformers.js](https://github.com/huggingface/transformers) and you likely are not looking for this
|
||||
> as it is specially used in the AnythingLLM Desktop application.
|
||||
|
||||
State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server!
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# This folder contains the build files for the documentation. Do not commit these files.
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"source": {
|
||||
"excludePattern": ""
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
|
||||
import re
|
||||
README_TEMPLATE = """
|
||||
|
||||
<p align="center">
|
||||
<br/>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/xenova/transformers.js/assets/26504141/bd047e0f-aca9-4ff7-ba07-c7ca55442bc4" width="500" style="max-width: 100%;">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://github.com/xenova/transformers.js/assets/26504141/84a5dc78-f4ea-43f4-96f2-b8c791f30a8e" width="500" style="max-width: 100%;">
|
||||
<img alt="transformers.js javascript library logo" src="https://github.com/xenova/transformers.js/assets/26504141/84a5dc78-f4ea-43f4-96f2-b8c791f30a8e" width="500" style="max-width: 100%;">
|
||||
</picture>
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/@xenova/transformers"><img alt="NPM" src="https://img.shields.io/npm/v/@xenova/transformers"></a>
|
||||
<a href="https://www.npmjs.com/package/@xenova/transformers"><img alt="NPM Downloads" src="https://img.shields.io/npm/dw/@xenova/transformers"></a>
|
||||
<a href="https://www.jsdelivr.com/package/npm/@xenova/transformers"><img alt="jsDelivr Hits" src="https://img.shields.io/jsdelivr/npm/hw/@xenova/transformers"></a>
|
||||
<a href="https://github.com/xenova/transformers.js/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/xenova/transformers.js?color=blue"></a>
|
||||
<a href="https://huggingface.co/docs/transformers.js/index"><img alt="Documentation" src="https://img.shields.io/website/http/huggingface.co/docs/transformers.js/index.svg?down_color=red&down_message=offline&up_message=online"></a>
|
||||
</p>
|
||||
|
||||
{intro}
|
||||
|
||||
## Quick tour
|
||||
|
||||
{quick_tour}
|
||||
|
||||
## Installation
|
||||
|
||||
{installation}
|
||||
|
||||
## Examples
|
||||
|
||||
{examples}
|
||||
|
||||
## Custom usage
|
||||
|
||||
{custom_usage}
|
||||
|
||||
## Supported tasks/models
|
||||
|
||||
Here is the list of all tasks and architectures currently supported by Transformers.js.
|
||||
If you don't see your task/model listed here or it is not yet supported, feel free
|
||||
to open up a feature request [here](https://github.com/xenova/transformers.js/issues/new/choose).
|
||||
|
||||
To find compatible models on the Hub, select the "transformers.js" library tag in the filter menu (or visit [this link](https://huggingface.co/models?library=transformers.js)).
|
||||
You can refine your search by selecting the task you're interested in (e.g., [text-classification](https://huggingface.co/models?pipeline_tag=text-classification&library=transformers.js)).
|
||||
|
||||
{tasks}
|
||||
|
||||
{models}
|
||||
"""
|
||||
|
||||
|
||||
FILES_TO_INCLUDE = dict(
|
||||
intro='./docs/snippets/0_introduction.snippet',
|
||||
quick_tour='./docs/snippets/1_quick-tour.snippet',
|
||||
installation='./docs/snippets/2_installation.snippet',
|
||||
examples='./docs/snippets/3_examples.snippet',
|
||||
custom_usage='./docs/snippets/4_custom-usage.snippet',
|
||||
tasks='./docs/snippets/5_supported-tasks.snippet',
|
||||
models='./docs/snippets/6_supported-models.snippet',
|
||||
)
|
||||
|
||||
DOCS_BASE_URL = 'https://huggingface.co/docs/transformers.js'
|
||||
|
||||
# Map of custom links to replace, typically used for links to other sections of the README.
|
||||
CUSTOM_LINK_MAP = {
|
||||
'/custom_usage#convert-your-models-to-onnx': '#convert-your-models-to-onnx',
|
||||
'./api/env': DOCS_BASE_URL + '/api/env',
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
file_data = {}
|
||||
for key, file_path in FILES_TO_INCLUDE.items():
|
||||
with open(file_path, encoding='utf-8') as f:
|
||||
file_data[key] = f.read()
|
||||
|
||||
# Fix links:
|
||||
# NOTE: This regex does not match all markdown links, but works for the ones we need to replace.
|
||||
LINK_RE = r'(?<=\])\((.+?)\)'
|
||||
|
||||
def replace_fn(match):
|
||||
link = match.group(1)
|
||||
|
||||
if link in CUSTOM_LINK_MAP:
|
||||
link = CUSTOM_LINK_MAP[link]
|
||||
|
||||
elif link.startswith('/'):
|
||||
# Link to docs
|
||||
link = DOCS_BASE_URL + link
|
||||
|
||||
elif link.startswith('./'):
|
||||
# Relative link to file
|
||||
pass
|
||||
|
||||
elif link.startswith('http'):
|
||||
# Link to external site
|
||||
pass
|
||||
|
||||
return f'({link})'
|
||||
|
||||
result = README_TEMPLATE.format(**file_data)
|
||||
result = re.sub(LINK_RE, replace_fn, result, 0, re.MULTILINE)
|
||||
|
||||
with open('README.md', 'w', encoding='utf-8') as f:
|
||||
f.write(result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,68 +0,0 @@
|
||||
// Based on [this tutorial](https://github.com/jsdoc2md/jsdoc-to-markdown/wiki/How-to-create-one-output-file-per-class).
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import url from 'url';
|
||||
|
||||
import jsdoc2md from 'jsdoc-to-markdown';
|
||||
|
||||
const docs = path.dirname(path.dirname(url.fileURLToPath(import.meta.url)));
|
||||
const root = path.dirname(docs);
|
||||
|
||||
// jsdoc config file
|
||||
const conf = path.join(docs, 'jsdoc-conf.json');
|
||||
|
||||
// input and output paths
|
||||
const inputFile = path.join(root, '/src/**/*.js');
|
||||
const outputDir = path.join(root, '/docs/source/api/');
|
||||
|
||||
|
||||
// get template data
|
||||
const templateData = jsdoc2md.getTemplateDataSync({
|
||||
files: inputFile,
|
||||
configure: conf
|
||||
})
|
||||
|
||||
// reduce templateData to an array of module names
|
||||
const moduleNames = templateData.reduce(
|
||||
(moduleNames, identifier) => {
|
||||
if (identifier.kind === 'module') {
|
||||
moduleNames.push(identifier.name)
|
||||
}
|
||||
return moduleNames
|
||||
}, []
|
||||
)
|
||||
|
||||
// create a documentation file for each module
|
||||
for (const moduleName of moduleNames) {
|
||||
const template = `{{#module name="${moduleName}"}}{{>docs}}{{/module}}`;
|
||||
console.log(`rendering ${moduleName}, template: ${template}`);
|
||||
let output = jsdoc2md.renderSync({
|
||||
'data': templateData,
|
||||
'template': template,
|
||||
'heading-depth': 1,
|
||||
'no-gfm': true,
|
||||
'name-format': 'backticks',
|
||||
'no-cache': true,
|
||||
'separators': true,
|
||||
'configure': conf,
|
||||
});
|
||||
|
||||
// Post-processing
|
||||
output = output.replace(/(^#+\s.+)/gm, '$1\n'); // Add new line after each header
|
||||
|
||||
// Replace all generated marker names with ids (for linking), and add group class
|
||||
output = output.replace(/<a name="(\S+)"><\/a>/g, '<a id="$1" class="group"></a>');
|
||||
|
||||
// Unescape some of the characters which jsdoc2md escapes:
|
||||
// TODO: May need to extend this list
|
||||
output = output.replace(/\\([|_&*])/gm, '$1');
|
||||
|
||||
output = output.replaceAll('new exports.', 'new ');
|
||||
|
||||
let outputPath = path.resolve(outputDir, `${moduleName}.md`);
|
||||
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, output);
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server!
|
||||
|
||||
Transformers.js is designed to be functionally equivalent to Hugging Face's [transformers](https://github.com/huggingface/transformers) python library, meaning you can run the same pretrained models using a very similar API. These models support common tasks in different modalities, such as:
|
||||
- 📝 **Natural Language Processing**: text classification, named entity recognition, question answering, language modeling, summarization, translation, multiple choice, and text generation.
|
||||
- 🖼️ **Computer Vision**: image classification, object detection, and segmentation.
|
||||
- 🗣️ **Audio**: automatic speech recognition and audio classification.
|
||||
- 🐙 **Multimodal**: zero-shot image classification.
|
||||
|
||||
Transformers.js uses [ONNX Runtime](https://onnxruntime.ai/) to run models in the browser. The best part about it, is that you can easily [convert](#convert-your-models-to-onnx) your pretrained PyTorch, TensorFlow, or JAX models to ONNX using [🤗 Optimum](https://github.com/huggingface/optimum#onnx--onnx-runtime).
|
||||
|
||||
For more information, check out the full [documentation](https://huggingface.co/docs/transformers.js).
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
It's super simple to translate from existing code! Just like the python library, we support the `pipeline` API. Pipelines group together a pretrained model with preprocessing of inputs and postprocessing of outputs, making it the easiest way to run models with the library.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th width="440px" align="center"><b>Python (original)</b></th>
|
||||
<th width="440px" align="center"><b>Javascript (ours)</b></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```python
|
||||
from transformers import pipeline
|
||||
|
||||
# Allocate a pipeline for sentiment-analysis
|
||||
pipe = pipeline('sentiment-analysis')
|
||||
|
||||
out = pipe('I love transformers!')
|
||||
# [{'label': 'POSITIVE', 'score': 0.999806941}]
|
||||
```
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```javascript
|
||||
import { pipeline } from '@xenova/transformers';
|
||||
|
||||
// Allocate a pipeline for sentiment-analysis
|
||||
let pipe = await pipeline('sentiment-analysis');
|
||||
|
||||
let out = await pipe('I love transformers!');
|
||||
// [{'label': 'POSITIVE', 'score': 0.999817686}]
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
You can also use a different model by specifying the model id or path as the second argument to the `pipeline` function. For example:
|
||||
```javascript
|
||||
// Use a different model for sentiment-analysis
|
||||
let pipe = await pipeline('sentiment-analysis', 'Xenova/bert-base-multilingual-uncased-sentiment');
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
To install via [NPM](https://www.npmjs.com/package/@xenova/transformers), run:
|
||||
```bash
|
||||
npm i @xenova/transformers
|
||||
```
|
||||
|
||||
Alternatively, you can use it in vanilla JS, without any bundler, by using a CDN or static hosting. For example, using [ES Modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), you can import the library with:
|
||||
```html
|
||||
<script type="module">
|
||||
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2';
|
||||
</script>
|
||||
```
|
||||
@@ -1,20 +0,0 @@
|
||||
Want to jump straight in? Get started with one of our sample applications/templates:
|
||||
|
||||
| Name | Description | Links |
|
||||
|-------------------|----------------------------------|-------------------------------|
|
||||
| Whisper Web | Speech recognition w/ Whisper | [code](https://github.com/xenova/whisper-web), [demo](https://huggingface.co/spaces/Xenova/whisper-web) |
|
||||
| Doodle Dash | Real-time sketch-recognition game | [blog](https://huggingface.co/blog/ml-web-games), [code](https://github.com/xenova/doodle-dash), [demo](https://huggingface.co/spaces/Xenova/doodle-dash) |
|
||||
| Code Playground | In-browser code completion website | [code](https://github.com/xenova/transformers.js/tree/main/examples/code-completion/), [demo](https://huggingface.co/spaces/Xenova/ai-code-playground) |
|
||||
| Semantic Image Search (client-side) | Search for images with text | [code](https://github.com/xenova/transformers.js/tree/main/examples/semantic-image-search-client/), [demo](https://huggingface.co/spaces/Xenova/semantic-image-search-client) |
|
||||
| Semantic Image Search (server-side) | Search for images with text (Supabase) | [code](https://github.com/xenova/transformers.js/tree/main/examples/semantic-image-search/), [demo](https://huggingface.co/spaces/Xenova/semantic-image-search) |
|
||||
| Vanilla JavaScript | In-browser object detection | [video](https://scrimba.com/scrim/cKm9bDAg), [code](https://github.com/xenova/transformers.js/tree/main/examples/vanilla-js/), [demo](https://huggingface.co/spaces/Scrimba/vanilla-js-object-detector) |
|
||||
| React | Multilingual translation website | [code](https://github.com/xenova/transformers.js/tree/main/examples/react-translator/), [demo](https://huggingface.co/spaces/Xenova/react-translator) |
|
||||
| Text to speech (client-side) | In-browser speech synthesis | [code](https://github.com/xenova/transformers.js/tree/main/examples/text-to-speech-client/), [demo](https://huggingface.co/spaces/Xenova/text-to-speech-client) |
|
||||
| Browser extension | Text classification extension | [code](https://github.com/xenova/transformers.js/tree/main/examples/extension/) |
|
||||
| Electron | Text classification application | [code](https://github.com/xenova/transformers.js/tree/main/examples/electron/) |
|
||||
| Next.js (client-side) | Sentiment analysis (in-browser inference) | [code](https://github.com/xenova/transformers.js/tree/main/examples/next-client/), [demo](https://huggingface.co/spaces/Xenova/next-example-app) |
|
||||
| Next.js (server-side) | Sentiment analysis (Node.js inference) | [code](https://github.com/xenova/transformers.js/tree/main/examples/next-server/), [demo](https://huggingface.co/spaces/Xenova/next-server-example-app) |
|
||||
| Node.js | Sentiment analysis API | [code](https://github.com/xenova/transformers.js/tree/main/examples/node/) |
|
||||
| Demo site | A collection of demos | [code](https://github.com/xenova/transformers.js/tree/main/examples/demo-site/), [demo](https://xenova.github.io/transformers.js/) |
|
||||
|
||||
Check out the Transformers.js [template](https://huggingface.co/new-space?template=static-templates%2Ftransformers.js) on Hugging Face to get started in one click!
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
|
||||
By default, Transformers.js uses [hosted pretrained models](https://huggingface.co/models?library=transformers.js) and [precompiled WASM binaries](https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2/dist/), which should work out-of-the-box. You can customize this as follows:
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
```javascript
|
||||
import { env } from '@xenova/transformers';
|
||||
|
||||
// Specify a custom location for models (defaults to '/models/').
|
||||
env.localModelPath = '/path/to/models/';
|
||||
|
||||
// Disable the loading of remote models from the Hugging Face Hub:
|
||||
env.allowRemoteModels = false;
|
||||
|
||||
// Set location of .wasm files. Defaults to use a CDN.
|
||||
env.backends.onnx.wasm.wasmPaths = '/path/to/files/';
|
||||
```
|
||||
|
||||
For a full list of available settings, check out the [API Reference](./api/env).
|
||||
|
||||
### Convert your models to ONNX
|
||||
|
||||
We recommend using our [conversion script](https://github.com/xenova/transformers.js/blob/main/scripts/convert.py) to convert your PyTorch, TensorFlow, or JAX models to ONNX in a single command. Behind the scenes, it uses [🤗 Optimum](https://huggingface.co/docs/optimum) to perform conversion and quantization of your model.
|
||||
|
||||
```bash
|
||||
python -m scripts.convert --quantize --model_id <model_name_or_path>
|
||||
```
|
||||
|
||||
For example, convert and quantize [bert-base-uncased](https://huggingface.co/bert-base-uncased) using:
|
||||
```bash
|
||||
python -m scripts.convert --quantize --model_id bert-base-uncased
|
||||
```
|
||||
|
||||
This will save the following files to `./models/`:
|
||||
|
||||
```
|
||||
bert-base-uncased/
|
||||
├── config.json
|
||||
├── tokenizer.json
|
||||
├── tokenizer_config.json
|
||||
└── onnx/
|
||||
├── model.onnx
|
||||
└── model_quantized.onnx
|
||||
```
|
||||
|
||||
For the full list of supported architectures, see the [Optimum documentation](https://huggingface.co/docs/optimum/main/en/exporters/onnx/overview).
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
### Tasks
|
||||
|
||||
#### Natural Language Processing
|
||||
|
||||
| Task | ID | Description | Supported? |
|
||||
|--------------------------|----|-------------|------------|
|
||||
| [Fill-Mask](https://huggingface.co/tasks/fill-mask) | `fill-mask` | Masking some of the words in a sentence and predicting which words should replace those masks. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.FillMaskPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=fill-mask&library=transformers.js) |
|
||||
| [Question Answering](https://huggingface.co/tasks/question-answering) | `question-answering` | Retrieve the answer to a question from a given text. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.QuestionAnsweringPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=question-answering&library=transformers.js) |
|
||||
| [Sentence Similarity](https://huggingface.co/tasks/sentence-similarity) | `sentence-similarity` | Determining how similar two texts are. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.FeatureExtractionPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js) |
|
||||
| [Summarization](https://huggingface.co/tasks/summarization) | `summarization` | Producing a shorter version of a document while preserving its important information. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.SummarizationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=summarization&library=transformers.js) |
|
||||
| [Table Question Answering](https://huggingface.co/tasks/table-question-answering) | `table-question-answering` | Answering a question about information from a given table. | ❌ |
|
||||
| [Text Classification](https://huggingface.co/tasks/text-classification) | `text-classification` or `sentiment-analysis` | Assigning a label or class to a given text. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.TextClassificationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=text-classification&library=transformers.js) |
|
||||
| [Text Generation](https://huggingface.co/tasks/text-generation#completion-generation-models) | `text-generation` | Producing new text by predicting the next word in a sequence. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.TextGenerationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=text-generation&library=transformers.js) |
|
||||
| [Text-to-text Generation](https://huggingface.co/tasks/text-generation#text-to-text-generation-models) | `text2text-generation` | Converting one text sequence into another text sequence. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.Text2TextGenerationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=text2text-generation&library=transformers.js) |
|
||||
| [Token Classification](https://huggingface.co/tasks/token-classification) | `token-classification` or `ner` | Assigning a label to each token in a text. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.TokenClassificationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=token-classification&library=transformers.js) |
|
||||
| [Translation](https://huggingface.co/tasks/translation) | `translation` | Converting text from one language to another. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.TranslationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=translation&library=transformers.js) |
|
||||
| [Zero-Shot Classification](https://huggingface.co/tasks/zero-shot-classification) | `zero-shot-classification` | Classifying text into classes that are unseen during training. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ZeroShotClassificationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=zero-shot-classification&library=transformers.js) |
|
||||
| [Feature Extraction](https://huggingface.co/tasks/feature-extraction) | `feature-extraction` | Transforming raw data into numerical features that can be processed while preserving the information in the original dataset. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.FeatureExtractionPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js) |
|
||||
|
||||
#### Vision
|
||||
|
||||
| Task | ID | Description | Supported? |
|
||||
|--------------------------|----|-------------|------------|
|
||||
| [Depth Estimation](https://huggingface.co/tasks/depth-estimation) | `depth-estimation` | Predicting the depth of objects present in an image. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.DepthEstimationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=depth-estimation&library=transformers.js) |
|
||||
| [Image Classification](https://huggingface.co/tasks/image-classification) | `image-classification` | Assigning a label or class to an entire image. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ImageClassificationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=image-classification&library=transformers.js) |
|
||||
| [Image Segmentation](https://huggingface.co/tasks/image-segmentation) | `image-segmentation` | Divides an image into segments where each pixel is mapped to an object. This task has multiple variants such as instance segmentation, panoptic segmentation and semantic segmentation. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ImageSegmentationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=image-segmentation&library=transformers.js) |
|
||||
| [Image-to-Image](https://huggingface.co/tasks/image-to-image) | `image-to-image` | Transforming a source image to match the characteristics of a target image or a target image domain. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ImageToImagePipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=image-to-image&library=transformers.js) |
|
||||
| [Mask Generation](https://huggingface.co/tasks/mask-generation) | `mask-generation` | Generate masks for the objects in an image. | ❌ |
|
||||
| [Object Detection](https://huggingface.co/tasks/object-detection) | `object-detection` | Identify objects of certain defined classes within an image. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ObjectDetectionPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=object-detection&library=transformers.js) |
|
||||
| [Video Classification](https://huggingface.co/tasks/video-classification) | n/a | Assigning a label or class to an entire video. | ❌ |
|
||||
| [Unconditional Image Generation](https://huggingface.co/tasks/unconditional-image-generation) | n/a | Generating images with no condition in any context (like a prompt text or another image). | ❌ |
|
||||
| [Image Feature Extraction](https://huggingface.co/tasks/image-feature-extraction) | `image-feature-extraction` | Transforming raw data into numerical features that can be processed while preserving the information in the original image. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ImageFeatureExtractionPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=image-feature-extraction&library=transformers.js) |
|
||||
|
||||
#### Audio
|
||||
|
||||
| Task | ID | Description | Supported? |
|
||||
|--------------------------|----|-------------|------------|
|
||||
| [Audio Classification](https://huggingface.co/tasks/audio-classification) | `audio-classification` | Assigning a label or class to a given audio. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.AudioClassificationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=audio-classification&library=transformers.js) |
|
||||
| [Audio-to-Audio](https://huggingface.co/tasks/audio-to-audio) | n/a | Generating audio from an input audio source. | ❌ |
|
||||
| [Automatic Speech Recognition](https://huggingface.co/tasks/automatic-speech-recognition) | `automatic-speech-recognition` | Transcribing a given audio into text. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.AutomaticSpeechRecognitionPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=automatic-speech-recognition&library=transformers.js) |
|
||||
| [Text-to-Speech](https://huggingface.co/tasks/text-to-speech) | `text-to-speech` or `text-to-audio` | Generating natural-sounding speech given text input. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.TextToAudioPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=text-to-audio&library=transformers.js) |
|
||||
|
||||
|
||||
#### Tabular
|
||||
|
||||
| Task | ID | Description | Supported? |
|
||||
|--------------------------|----|-------------|------------|
|
||||
| [Tabular Classification](https://huggingface.co/tasks/tabular-classification) | n/a | Classifying a target category (a group) based on set of attributes. | ❌ |
|
||||
| [Tabular Regression](https://huggingface.co/tasks/tabular-regression) | n/a | Predicting a numerical value given a set of attributes. | ❌ |
|
||||
|
||||
|
||||
#### Multimodal
|
||||
|
||||
| Task | ID | Description | Supported? |
|
||||
|--------------------------|----|-------------|------------|
|
||||
| [Document Question Answering](https://huggingface.co/tasks/document-question-answering) | `document-question-answering` | Answering questions on document images. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.DocumentQuestionAnsweringPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=document-question-answering&library=transformers.js) |
|
||||
| [Image-to-Text](https://huggingface.co/tasks/image-to-text) | `image-to-text` | Output text from a given image. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ImageToTextPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=image-to-text&library=transformers.js) |
|
||||
| [Text-to-Image](https://huggingface.co/tasks/text-to-image) | `text-to-image` | Generates images from input text. | ❌ |
|
||||
| [Visual Question Answering](https://huggingface.co/tasks/visual-question-answering) | `visual-question-answering` | Answering open-ended questions based on an image. | ❌ |
|
||||
| [Zero-Shot Audio Classification](https://huggingface.co/learn/audio-course/chapter4/classification_models#zero-shot-audio-classification) | `zero-shot-audio-classification` | Classifying audios into classes that are unseen during training. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ZeroShotAudioClassificationPipeline)<br>[(models)](https://huggingface.co/models?other=zero-shot-audio-classification&library=transformers.js) |
|
||||
| [Zero-Shot Image Classification](https://huggingface.co/tasks/zero-shot-image-classification) | `zero-shot-image-classification` | Classifying images into classes that are unseen during training. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ZeroShotImageClassificationPipeline)<br>[(models)](https://huggingface.co/models?pipeline_tag=zero-shot-image-classification&library=transformers.js) |
|
||||
| [Zero-Shot Object Detection](https://huggingface.co/tasks/zero-shot-object-detection) | `zero-shot-object-detection` | Identify objects of classes that are unseen during training. | ✅ [(docs)](https://huggingface.co/docs/transformers.js/api/pipelines#module_pipelines.ZeroShotObjectDetectionPipeline)<br>[(models)](https://huggingface.co/models?other=zero-shot-object-detection&library=transformers.js) |
|
||||
|
||||
|
||||
#### Reinforcement Learning
|
||||
|
||||
| Task | ID | Description | Supported? |
|
||||
|--------------------------|----|-------------|------------|
|
||||
| [Reinforcement Learning](https://huggingface.co/tasks/reinforcement-learning) | n/a | Learning from actions by interacting with an environment through trial and error and receiving rewards (negative or positive) as feedback. | ✅ |
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
### Models
|
||||
|
||||
1. **[ALBERT](https://huggingface.co/docs/transformers/model_doc/albert)** (from Google Research and the Toyota Technological Institute at Chicago) released with the paper [ALBERT: A Lite BERT for Self-supervised Learning of Language Representations](https://arxiv.org/abs/1909.11942), by Zhenzhong Lan, Mingda Chen, Sebastian Goodman, Kevin Gimpel, Piyush Sharma, Radu Soricut.
|
||||
1. **[Audio Spectrogram Transformer](https://huggingface.co/docs/transformers/model_doc/audio-spectrogram-transformer)** (from MIT) released with the paper [AST: Audio Spectrogram Transformer](https://arxiv.org/abs/2104.01778) by Yuan Gong, Yu-An Chung, James Glass.
|
||||
1. **[BART](https://huggingface.co/docs/transformers/model_doc/bart)** (from Facebook) released with the paper [BART: Denoising Sequence-to-Sequence Pre-training for Natural Language Generation, Translation, and Comprehension](https://arxiv.org/abs/1910.13461) by Mike Lewis, Yinhan Liu, Naman Goyal, Marjan Ghazvininejad, Abdelrahman Mohamed, Omer Levy, Ves Stoyanov and Luke Zettlemoyer.
|
||||
1. **[BEiT](https://huggingface.co/docs/transformers/model_doc/beit)** (from Microsoft) released with the paper [BEiT: BERT Pre-Training of Image Transformers](https://arxiv.org/abs/2106.08254) by Hangbo Bao, Li Dong, Furu Wei.
|
||||
1. **[BERT](https://huggingface.co/docs/transformers/model_doc/bert)** (from Google) released with the paper [BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding](https://arxiv.org/abs/1810.04805) by Jacob Devlin, Ming-Wei Chang, Kenton Lee and Kristina Toutanova.
|
||||
1. **[Blenderbot](https://huggingface.co/docs/transformers/model_doc/blenderbot)** (from Facebook) released with the paper [Recipes for building an open-domain chatbot](https://arxiv.org/abs/2004.13637) by Stephen Roller, Emily Dinan, Naman Goyal, Da Ju, Mary Williamson, Yinhan Liu, Jing Xu, Myle Ott, Kurt Shuster, Eric M. Smith, Y-Lan Boureau, Jason Weston.
|
||||
1. **[BlenderbotSmall](https://huggingface.co/docs/transformers/model_doc/blenderbot-small)** (from Facebook) released with the paper [Recipes for building an open-domain chatbot](https://arxiv.org/abs/2004.13637) by Stephen Roller, Emily Dinan, Naman Goyal, Da Ju, Mary Williamson, Yinhan Liu, Jing Xu, Myle Ott, Kurt Shuster, Eric M. Smith, Y-Lan Boureau, Jason Weston.
|
||||
1. **[BLOOM](https://huggingface.co/docs/transformers/model_doc/bloom)** (from BigScience workshop) released by the [BigScience Workshop](https://bigscience.huggingface.co/).
|
||||
1. **[CamemBERT](https://huggingface.co/docs/transformers/model_doc/camembert)** (from Inria/Facebook/Sorbonne) released with the paper [CamemBERT: a Tasty French Language Model](https://arxiv.org/abs/1911.03894) by Louis Martin*, Benjamin Muller*, Pedro Javier Ortiz Suárez*, Yoann Dupont, Laurent Romary, Éric Villemonte de la Clergerie, Djamé Seddah and Benoît Sagot.
|
||||
1. **[Chinese-CLIP](https://huggingface.co/docs/transformers/model_doc/chinese_clip)** (from OFA-Sys) released with the paper [Chinese CLIP: Contrastive Vision-Language Pretraining in Chinese](https://arxiv.org/abs/2211.01335) by An Yang, Junshu Pan, Junyang Lin, Rui Men, Yichang Zhang, Jingren Zhou, Chang Zhou.
|
||||
1. **[CLAP](https://huggingface.co/docs/transformers/model_doc/clap)** (from LAION-AI) released with the paper [Large-scale Contrastive Language-Audio Pretraining with Feature Fusion and Keyword-to-Caption Augmentation](https://arxiv.org/abs/2211.06687) by Yusong Wu, Ke Chen, Tianyu Zhang, Yuchen Hui, Taylor Berg-Kirkpatrick, Shlomo Dubnov.
|
||||
1. **[CLIP](https://huggingface.co/docs/transformers/model_doc/clip)** (from OpenAI) released with the paper [Learning Transferable Visual Models From Natural Language Supervision](https://arxiv.org/abs/2103.00020) by Alec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal, Girish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, Gretchen Krueger, Ilya Sutskever.
|
||||
1. **[CLIPSeg](https://huggingface.co/docs/transformers/model_doc/clipseg)** (from University of Göttingen) released with the paper [Image Segmentation Using Text and Image Prompts](https://arxiv.org/abs/2112.10003) by Timo Lüddecke and Alexander Ecker.
|
||||
1. **[CodeGen](https://huggingface.co/docs/transformers/model_doc/codegen)** (from Salesforce) released with the paper [A Conversational Paradigm for Program Synthesis](https://arxiv.org/abs/2203.13474) by Erik Nijkamp, Bo Pang, Hiroaki Hayashi, Lifu Tu, Huan Wang, Yingbo Zhou, Silvio Savarese, Caiming Xiong.
|
||||
1. **[CodeLlama](https://huggingface.co/docs/transformers/model_doc/llama_code)** (from MetaAI) released with the paper [Code Llama: Open Foundation Models for Code](https://ai.meta.com/research/publications/code-llama-open-foundation-models-for-code/) by Baptiste Rozière, Jonas Gehring, Fabian Gloeckle, Sten Sootla, Itai Gat, Xiaoqing Ellen Tan, Yossi Adi, Jingyu Liu, Tal Remez, Jérémy Rapin, Artyom Kozhevnikov, Ivan Evtimov, Joanna Bitton, Manish Bhatt, Cristian Canton Ferrer, Aaron Grattafiori, Wenhan Xiong, Alexandre Défossez, Jade Copet, Faisal Azhar, Hugo Touvron, Louis Martin, Nicolas Usunier, Thomas Scialom, Gabriel Synnaeve.
|
||||
1. **[ConvBERT](https://huggingface.co/docs/transformers/model_doc/convbert)** (from YituTech) released with the paper [ConvBERT: Improving BERT with Span-based Dynamic Convolution](https://arxiv.org/abs/2008.02496) by Zihang Jiang, Weihao Yu, Daquan Zhou, Yunpeng Chen, Jiashi Feng, Shuicheng Yan.
|
||||
1. **[ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext)** (from Facebook AI) released with the paper [A ConvNet for the 2020s](https://arxiv.org/abs/2201.03545) by Zhuang Liu, Hanzi Mao, Chao-Yuan Wu, Christoph Feichtenhofer, Trevor Darrell, Saining Xie.
|
||||
1. **[ConvNeXTV2](https://huggingface.co/docs/transformers/model_doc/convnextv2)** (from Facebook AI) released with the paper [ConvNeXt V2: Co-designing and Scaling ConvNets with Masked Autoencoders](https://arxiv.org/abs/2301.00808) by Sanghyun Woo, Shoubhik Debnath, Ronghang Hu, Xinlei Chen, Zhuang Liu, In So Kweon, Saining Xie.
|
||||
1. **[DeBERTa](https://huggingface.co/docs/transformers/model_doc/deberta)** (from Microsoft) released with the paper [DeBERTa: Decoding-enhanced BERT with Disentangled Attention](https://arxiv.org/abs/2006.03654) by Pengcheng He, Xiaodong Liu, Jianfeng Gao, Weizhu Chen.
|
||||
1. **[DeBERTa-v2](https://huggingface.co/docs/transformers/model_doc/deberta-v2)** (from Microsoft) released with the paper [DeBERTa: Decoding-enhanced BERT with Disentangled Attention](https://arxiv.org/abs/2006.03654) by Pengcheng He, Xiaodong Liu, Jianfeng Gao, Weizhu Chen.
|
||||
1. **[Decision Transformer](https://huggingface.co/docs/transformers/model_doc/decision_transformer)** (from Berkeley/Facebook/Google) released with the paper [Decision Transformer: Reinforcement Learning via Sequence Modeling](https://arxiv.org/abs/2106.01345) by Lili Chen, Kevin Lu, Aravind Rajeswaran, Kimin Lee, Aditya Grover, Michael Laskin, Pieter Abbeel, Aravind Srinivas, Igor Mordatch.
|
||||
1. **[DeiT](https://huggingface.co/docs/transformers/model_doc/deit)** (from Facebook) released with the paper [Training data-efficient image transformers & distillation through attention](https://arxiv.org/abs/2012.12877) by Hugo Touvron, Matthieu Cord, Matthijs Douze, Francisco Massa, Alexandre Sablayrolles, Hervé Jégou.
|
||||
1. **[Depth Anything](https://huggingface.co/docs/transformers/main/model_doc/depth_anything)** (from University of Hong Kong and TikTok) released with the paper [Depth Anything: Unleashing the Power of Large-Scale Unlabeled Data](https://arxiv.org/abs/2401.10891) by Lihe Yang, Bingyi Kang, Zilong Huang, Xiaogang Xu, Jiashi Feng, Hengshuang Zhao.
|
||||
1. **[DETR](https://huggingface.co/docs/transformers/model_doc/detr)** (from Facebook) released with the paper [End-to-End Object Detection with Transformers](https://arxiv.org/abs/2005.12872) by Nicolas Carion, Francisco Massa, Gabriel Synnaeve, Nicolas Usunier, Alexander Kirillov, Sergey Zagoruyko.
|
||||
1. **[DINOv2](https://huggingface.co/docs/transformers/model_doc/dinov2)** (from Meta AI) released with the paper [DINOv2: Learning Robust Visual Features without Supervision](https://arxiv.org/abs/2304.07193) by Maxime Oquab, Timothée Darcet, Théo Moutakanni, Huy Vo, Marc Szafraniec, Vasil Khalidov, Pierre Fernandez, Daniel Haziza, Francisco Massa, Alaaeldin El-Nouby, Mahmoud Assran, Nicolas Ballas, Wojciech Galuba, Russell Howes, Po-Yao Huang, Shang-Wen Li, Ishan Misra, Michael Rabbat, Vasu Sharma, Gabriel Synnaeve, Hu Xu, Hervé Jegou, Julien Mairal, Patrick Labatut, Armand Joulin, Piotr Bojanowski.
|
||||
1. **[DistilBERT](https://huggingface.co/docs/transformers/model_doc/distilbert)** (from HuggingFace), released together with the paper [DistilBERT, a distilled version of BERT: smaller, faster, cheaper and lighter](https://arxiv.org/abs/1910.01108) by Victor Sanh, Lysandre Debut and Thomas Wolf. The same method has been applied to compress GPT2 into [DistilGPT2](https://github.com/huggingface/transformers/tree/main/examples/research_projects/distillation), RoBERTa into [DistilRoBERTa](https://github.com/huggingface/transformers/tree/main/examples/research_projects/distillation), Multilingual BERT into [DistilmBERT](https://github.com/huggingface/transformers/tree/main/examples/research_projects/distillation) and a German version of DistilBERT.
|
||||
1. **[DiT](https://huggingface.co/docs/transformers/model_doc/dit)** (from Microsoft Research) released with the paper [DiT: Self-supervised Pre-training for Document Image Transformer](https://arxiv.org/abs/2203.02378) by Junlong Li, Yiheng Xu, Tengchao Lv, Lei Cui, Cha Zhang, Furu Wei.
|
||||
1. **[Donut](https://huggingface.co/docs/transformers/model_doc/donut)** (from NAVER), released together with the paper [OCR-free Document Understanding Transformer](https://arxiv.org/abs/2111.15664) by Geewook Kim, Teakgyu Hong, Moonbin Yim, Jeongyeon Nam, Jinyoung Park, Jinyeong Yim, Wonseok Hwang, Sangdoo Yun, Dongyoon Han, Seunghyun Park.
|
||||
1. **[DPT](https://huggingface.co/docs/transformers/master/model_doc/dpt)** (from Intel Labs) released with the paper [Vision Transformers for Dense Prediction](https://arxiv.org/abs/2103.13413) by René Ranftl, Alexey Bochkovskiy, Vladlen Koltun.
|
||||
1. **[EfficientNet](https://huggingface.co/docs/transformers/model_doc/efficientnet)** (from Google Brain) released with the paper [EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks](https://arxiv.org/abs/1905.11946) by Mingxing Tan, Quoc V. Le.
|
||||
1. **[ELECTRA](https://huggingface.co/docs/transformers/model_doc/electra)** (from Google Research/Stanford University) released with the paper [ELECTRA: Pre-training text encoders as discriminators rather than generators](https://arxiv.org/abs/2003.10555) by Kevin Clark, Minh-Thang Luong, Quoc V. Le, Christopher D. Manning.
|
||||
1. **[ESM](https://huggingface.co/docs/transformers/model_doc/esm)** (from Meta AI) are transformer protein language models. **ESM-1b** was released with the paper [Biological structure and function emerge from scaling unsupervised learning to 250 million protein sequences](https://www.pnas.org/content/118/15/e2016239118) by Alexander Rives, Joshua Meier, Tom Sercu, Siddharth Goyal, Zeming Lin, Jason Liu, Demi Guo, Myle Ott, C. Lawrence Zitnick, Jerry Ma, and Rob Fergus. **ESM-1v** was released with the paper [Language models enable zero-shot prediction of the effects of mutations on protein function](https://doi.org/10.1101/2021.07.09.450648) by Joshua Meier, Roshan Rao, Robert Verkuil, Jason Liu, Tom Sercu and Alexander Rives. **ESM-2 and ESMFold** were released with the paper [Language models of protein sequences at the scale of evolution enable accurate structure prediction](https://doi.org/10.1101/2022.07.20.500902) by Zeming Lin, Halil Akin, Roshan Rao, Brian Hie, Zhongkai Zhu, Wenting Lu, Allan dos Santos Costa, Maryam Fazel-Zarandi, Tom Sercu, Sal Candido, Alexander Rives.
|
||||
1. **[Falcon](https://huggingface.co/docs/transformers/model_doc/falcon)** (from Technology Innovation Institute) by Almazrouei, Ebtesam and Alobeidli, Hamza and Alshamsi, Abdulaziz and Cappelli, Alessandro and Cojocaru, Ruxandra and Debbah, Merouane and Goffinet, Etienne and Heslow, Daniel and Launay, Julien and Malartic, Quentin and Noune, Badreddine and Pannier, Baptiste and Penedo, Guilherme.
|
||||
1. **FastViT** (from Apple) released with the paper [FastViT: A Fast Hybrid Vision Transformer using Structural Reparameterization](https://arxiv.org/abs/2303.14189) by Pavan Kumar Anasosalu Vasu, James Gabriel, Jeff Zhu, Oncel Tuzel and Anurag Ranjan.
|
||||
1. **[FLAN-T5](https://huggingface.co/docs/transformers/model_doc/flan-t5)** (from Google AI) released in the repository [google-research/t5x](https://github.com/google-research/t5x/blob/main/docs/models.md#flan-t5-checkpoints) by Hyung Won Chung, Le Hou, Shayne Longpre, Barret Zoph, Yi Tay, William Fedus, Eric Li, Xuezhi Wang, Mostafa Dehghani, Siddhartha Brahma, Albert Webson, Shixiang Shane Gu, Zhuyun Dai, Mirac Suzgun, Xinyun Chen, Aakanksha Chowdhery, Sharan Narang, Gaurav Mishra, Adams Yu, Vincent Zhao, Yanping Huang, Andrew Dai, Hongkun Yu, Slav Petrov, Ed H. Chi, Jeff Dean, Jacob Devlin, Adam Roberts, Denny Zhou, Quoc V. Le, and Jason Wei
|
||||
1. **[GLPN](https://huggingface.co/docs/transformers/model_doc/glpn)** (from KAIST) released with the paper [Global-Local Path Networks for Monocular Depth Estimation with Vertical CutDepth](https://arxiv.org/abs/2201.07436) by Doyeon Kim, Woonghyun Ga, Pyungwhan Ahn, Donggyu Joo, Sehwan Chun, Junmo Kim.
|
||||
1. **[GPT Neo](https://huggingface.co/docs/transformers/model_doc/gpt_neo)** (from EleutherAI) released in the repository [EleutherAI/gpt-neo](https://github.com/EleutherAI/gpt-neo) by Sid Black, Stella Biderman, Leo Gao, Phil Wang and Connor Leahy.
|
||||
1. **[GPT NeoX](https://huggingface.co/docs/transformers/model_doc/gpt_neox)** (from EleutherAI) released with the paper [GPT-NeoX-20B: An Open-Source Autoregressive Language Model](https://arxiv.org/abs/2204.06745) by Sid Black, Stella Biderman, Eric Hallahan, Quentin Anthony, Leo Gao, Laurence Golding, Horace He, Connor Leahy, Kyle McDonell, Jason Phang, Michael Pieler, USVSN Sai Prashanth, Shivanshu Purohit, Laria Reynolds, Jonathan Tow, Ben Wang, Samuel Weinbach
|
||||
1. **[GPT-2](https://huggingface.co/docs/transformers/model_doc/gpt2)** (from OpenAI) released with the paper [Language Models are Unsupervised Multitask Learners](https://blog.openai.com/better-language-models/) by Alec Radford*, Jeffrey Wu*, Rewon Child, David Luan, Dario Amodei** and Ilya Sutskever**.
|
||||
1. **[GPT-J](https://huggingface.co/docs/transformers/model_doc/gptj)** (from EleutherAI) released in the repository [kingoflolz/mesh-transformer-jax](https://github.com/kingoflolz/mesh-transformer-jax/) by Ben Wang and Aran Komatsuzaki.
|
||||
1. **[GPTBigCode](https://huggingface.co/docs/transformers/model_doc/gpt_bigcode)** (from BigCode) released with the paper [SantaCoder: don't reach for the stars!](https://arxiv.org/abs/2301.03988) by Loubna Ben Allal, Raymond Li, Denis Kocetkov, Chenghao Mou, Christopher Akiki, Carlos Munoz Ferrandis, Niklas Muennighoff, Mayank Mishra, Alex Gu, Manan Dey, Logesh Kumar Umapathi, Carolyn Jane Anderson, Yangtian Zi, Joel Lamy Poirier, Hailey Schoelkopf, Sergey Troshin, Dmitry Abulkhanov, Manuel Romero, Michael Lappert, Francesco De Toni, Bernardo García del Río, Qian Liu, Shamik Bose, Urvashi Bhattacharyya, Terry Yue Zhuo, Ian Yu, Paulo Villegas, Marco Zocca, Sourab Mangrulkar, David Lansky, Huu Nguyen, Danish Contractor, Luis Villa, Jia Li, Dzmitry Bahdanau, Yacine Jernite, Sean Hughes, Daniel Fried, Arjun Guha, Harm de Vries, Leandro von Werra.
|
||||
1. **[HerBERT](https://huggingface.co/docs/transformers/model_doc/herbert)** (from Allegro.pl, AGH University of Science and Technology) released with the paper [KLEJ: Comprehensive Benchmark for Polish Language Understanding](https://www.aclweb.org/anthology/2020.acl-main.111.pdf) by Piotr Rybak, Robert Mroczkowski, Janusz Tracz, Ireneusz Gawlik.
|
||||
1. **[Hubert](https://huggingface.co/docs/transformers/model_doc/hubert)** (from Facebook) released with the paper [HuBERT: Self-Supervised Speech Representation Learning by Masked Prediction of Hidden Units](https://arxiv.org/abs/2106.07447) by Wei-Ning Hsu, Benjamin Bolte, Yao-Hung Hubert Tsai, Kushal Lakhotia, Ruslan Salakhutdinov, Abdelrahman Mohamed.
|
||||
1. **[LongT5](https://huggingface.co/docs/transformers/model_doc/longt5)** (from Google AI) released with the paper [LongT5: Efficient Text-To-Text Transformer for Long Sequences](https://arxiv.org/abs/2112.07916) by Mandy Guo, Joshua Ainslie, David Uthus, Santiago Ontanon, Jianmo Ni, Yun-Hsuan Sung, Yinfei Yang.
|
||||
1. **[LLaMA](https://huggingface.co/docs/transformers/model_doc/llama)** (from The FAIR team of Meta AI) released with the paper [LLaMA: Open and Efficient Foundation Language Models](https://arxiv.org/abs/2302.13971) by Hugo Touvron, Thibaut Lavril, Gautier Izacard, Xavier Martinet, Marie-Anne Lachaux, Timothée Lacroix, Baptiste Rozière, Naman Goyal, Eric Hambro, Faisal Azhar, Aurelien Rodriguez, Armand Joulin, Edouard Grave, Guillaume Lample.
|
||||
1. **[Llama2](https://huggingface.co/docs/transformers/model_doc/llama2)** (from The FAIR team of Meta AI) released with the paper [Llama2: Open Foundation and Fine-Tuned Chat Models](https://ai.meta.com/research/publications/llama-2-open-foundation-and-fine-tuned-chat-models/XXX) by Hugo Touvron, Louis Martin, Kevin Stone, Peter Albert, Amjad Almahairi, Yasmine Babaei, Nikolay Bashlykov, Soumya Batra, Prajjwal Bhargava, Shruti Bhosale, Dan Bikel, Lukas Blecher, Cristian Canton Ferrer, Moya Chen, Guillem Cucurull, David Esiobu, Jude Fernandes, Jeremy Fu, Wenyin Fu, Brian Fuller, Cynthia Gao, Vedanuj Goswami, Naman Goyal, Anthony Hartshorn, Saghar Hosseini, Rui Hou, Hakan Inan, Marcin Kardas, Viktor Kerkez Madian Khabsa, Isabel Kloumann, Artem Korenev, Punit Singh Koura, Marie-Anne Lachaux, Thibaut Lavril, Jenya Lee, Diana Liskovich, Yinghai Lu, Yuning Mao, Xavier Martinet, Todor Mihaylov, Pushka rMishra, Igor Molybog, Yixin Nie, Andrew Poulton, Jeremy Reizenstein, Rashi Rungta, Kalyan Saladi, Alan Schelten, Ruan Silva, Eric Michael Smith, Ranjan Subramanian, Xiaoqing EllenTan, Binh Tang, Ross Taylor, Adina Williams, Jian Xiang Kuan, Puxin Xu, Zheng Yan, Iliyan Zarov, Yuchen Zhang, Angela Fan, Melanie Kambadur, Sharan Narang, Aurelien Rodriguez, Robert Stojnic, Sergey Edunov, Thomas Scialom.
|
||||
1. **[M2M100](https://huggingface.co/docs/transformers/model_doc/m2m_100)** (from Facebook) released with the paper [Beyond English-Centric Multilingual Machine Translation](https://arxiv.org/abs/2010.11125) by Angela Fan, Shruti Bhosale, Holger Schwenk, Zhiyi Ma, Ahmed El-Kishky, Siddharth Goyal, Mandeep Baines, Onur Celebi, Guillaume Wenzek, Vishrav Chaudhary, Naman Goyal, Tom Birch, Vitaliy Liptchinsky, Sergey Edunov, Edouard Grave, Michael Auli, Armand Joulin.
|
||||
1. **[MarianMT](https://huggingface.co/docs/transformers/model_doc/marian)** Machine translation models trained using [OPUS](http://opus.nlpl.eu/) data by Jörg Tiedemann. The [Marian Framework](https://marian-nmt.github.io/) is being developed by the Microsoft Translator Team.
|
||||
1. **[mBART](https://huggingface.co/docs/transformers/model_doc/mbart)** (from Facebook) released with the paper [Multilingual Denoising Pre-training for Neural Machine Translation](https://arxiv.org/abs/2001.08210) by Yinhan Liu, Jiatao Gu, Naman Goyal, Xian Li, Sergey Edunov, Marjan Ghazvininejad, Mike Lewis, Luke Zettlemoyer.
|
||||
1. **[mBART-50](https://huggingface.co/docs/transformers/model_doc/mbart)** (from Facebook) released with the paper [Multilingual Translation with Extensible Multilingual Pretraining and Finetuning](https://arxiv.org/abs/2008.00401) by Yuqing Tang, Chau Tran, Xian Li, Peng-Jen Chen, Naman Goyal, Vishrav Chaudhary, Jiatao Gu, Angela Fan.
|
||||
1. **[Mistral](https://huggingface.co/docs/transformers/model_doc/mistral)** (from Mistral AI) by The [Mistral AI](https://mistral.ai) team: Albert Jiang, Alexandre Sablayrolles, Arthur Mensch, Chris Bamford, Devendra Singh Chaplot, Diego de las Casas, Florian Bressand, Gianna Lengyel, Guillaume Lample, Lélio Renard Lavaud, Lucile Saulnier, Marie-Anne Lachaux, Pierre Stock, Teven Le Scao, Thibaut Lavril, Thomas Wang, Timothée Lacroix, William El Sayed.
|
||||
1. **[MMS](https://huggingface.co/docs/transformers/model_doc/mms)** (from Facebook) released with the paper [Scaling Speech Technology to 1,000+ Languages](https://arxiv.org/abs/2305.13516) by Vineel Pratap, Andros Tjandra, Bowen Shi, Paden Tomasello, Arun Babu, Sayani Kundu, Ali Elkahky, Zhaoheng Ni, Apoorv Vyas, Maryam Fazel-Zarandi, Alexei Baevski, Yossi Adi, Xiaohui Zhang, Wei-Ning Hsu, Alexis Conneau, Michael Auli.
|
||||
1. **[MobileBERT](https://huggingface.co/docs/transformers/model_doc/mobilebert)** (from CMU/Google Brain) released with the paper [MobileBERT: a Compact Task-Agnostic BERT for Resource-Limited Devices](https://arxiv.org/abs/2004.02984) by Zhiqing Sun, Hongkun Yu, Xiaodan Song, Renjie Liu, Yiming Yang, and Denny Zhou.
|
||||
1. **[MobileViT](https://huggingface.co/docs/transformers/model_doc/mobilevit)** (from Apple) released with the paper [MobileViT: Light-weight, General-purpose, and Mobile-friendly Vision Transformer](https://arxiv.org/abs/2110.02178) by Sachin Mehta and Mohammad Rastegari.
|
||||
1. **[MobileViTV2](https://huggingface.co/docs/transformers/model_doc/mobilevitv2)** (from Apple) released with the paper [Separable Self-attention for Mobile Vision Transformers](https://arxiv.org/abs/2206.02680) by Sachin Mehta and Mohammad Rastegari.
|
||||
1. **[MPNet](https://huggingface.co/docs/transformers/model_doc/mpnet)** (from Microsoft Research) released with the paper [MPNet: Masked and Permuted Pre-training for Language Understanding](https://arxiv.org/abs/2004.09297) by Kaitao Song, Xu Tan, Tao Qin, Jianfeng Lu, Tie-Yan Liu.
|
||||
1. **[MPT](https://huggingface.co/docs/transformers/model_doc/mpt)** (from MosaiML) released with the repository [llm-foundry](https://github.com/mosaicml/llm-foundry/) by the MosaicML NLP Team.
|
||||
1. **[MT5](https://huggingface.co/docs/transformers/model_doc/mt5)** (from Google AI) released with the paper [mT5: A massively multilingual pre-trained text-to-text transformer](https://arxiv.org/abs/2010.11934) by Linting Xue, Noah Constant, Adam Roberts, Mihir Kale, Rami Al-Rfou, Aditya Siddhant, Aditya Barua, Colin Raffel.
|
||||
1. **[NLLB](https://huggingface.co/docs/transformers/model_doc/nllb)** (from Meta) released with the paper [No Language Left Behind: Scaling Human-Centered Machine Translation](https://arxiv.org/abs/2207.04672) by the NLLB team.
|
||||
1. **[Nougat](https://huggingface.co/docs/transformers/model_doc/nougat)** (from Meta AI) released with the paper [Nougat: Neural Optical Understanding for Academic Documents](https://arxiv.org/abs/2308.13418) by Lukas Blecher, Guillem Cucurull, Thomas Scialom, Robert Stojnic.
|
||||
1. **[OPT](https://huggingface.co/docs/transformers/master/model_doc/opt)** (from Meta AI) released with the paper [OPT: Open Pre-trained Transformer Language Models](https://arxiv.org/abs/2205.01068) by Susan Zhang, Stephen Roller, Naman Goyal, Mikel Artetxe, Moya Chen, Shuohui Chen et al.
|
||||
1. **[OWL-ViT](https://huggingface.co/docs/transformers/model_doc/owlvit)** (from Google AI) released with the paper [Simple Open-Vocabulary Object Detection with Vision Transformers](https://arxiv.org/abs/2205.06230) by Matthias Minderer, Alexey Gritsenko, Austin Stone, Maxim Neumann, Dirk Weissenborn, Alexey Dosovitskiy, Aravindh Mahendran, Anurag Arnab, Mostafa Dehghani, Zhuoran Shen, Xiao Wang, Xiaohua Zhai, Thomas Kipf, and Neil Houlsby.
|
||||
1. **[OWLv2](https://huggingface.co/docs/transformers/model_doc/owlv2)** (from Google AI) released with the paper [Scaling Open-Vocabulary Object Detection](https://arxiv.org/abs/2306.09683) by Matthias Minderer, Alexey Gritsenko, Neil Houlsby.
|
||||
1. **[Phi](https://huggingface.co/docs/transformers/main/model_doc/phi)** (from Microsoft) released with the papers - [Textbooks Are All You Need](https://arxiv.org/abs/2306.11644) by Suriya Gunasekar, Yi Zhang, Jyoti Aneja, Caio César Teodoro Mendes, Allie Del Giorno, Sivakanth Gopi, Mojan Javaheripi, Piero Kauffmann, Gustavo de Rosa, Olli Saarikivi, Adil Salim, Shital Shah, Harkirat Singh Behl, Xin Wang, Sébastien Bubeck, Ronen Eldan, Adam Tauman Kalai, Yin Tat Lee and Yuanzhi Li, [Textbooks Are All You Need II: phi-1.5 technical report](https://arxiv.org/abs/2309.05463) by Yuanzhi Li, Sébastien Bubeck, Ronen Eldan, Allie Del Giorno, Suriya Gunasekar and Yin Tat Lee.
|
||||
1. **[Qwen2](https://huggingface.co/docs/transformers/model_doc/qwen2)** (from the Qwen team, Alibaba Group) released with the paper [Qwen Technical Report](https://arxiv.org/abs/2309.16609) by Jinze Bai, Shuai Bai, Yunfei Chu, Zeyu Cui, Kai Dang, Xiaodong Deng, Yang Fan, Wenbin Ge, Yu Han, Fei Huang, Binyuan Hui, Luo Ji, Mei Li, Junyang Lin, Runji Lin, Dayiheng Liu, Gao Liu, Chengqiang Lu, Keming Lu, Jianxin Ma, Rui Men, Xingzhang Ren, Xuancheng Ren, Chuanqi Tan, Sinan Tan, Jianhong Tu, Peng Wang, Shijie Wang, Wei Wang, Shengguang Wu, Benfeng Xu, Jin Xu, An Yang, Hao Yang, Jian Yang, Shusheng Yang, Yang Yao, Bowen Yu, Hongyi Yuan, Zheng Yuan, Jianwei Zhang, Xingxuan Zhang, Yichang Zhang, Zhenru Zhang, Chang Zhou, Jingren Zhou, Xiaohuan Zhou and Tianhang Zhu.
|
||||
1. **[ResNet](https://huggingface.co/docs/transformers/model_doc/resnet)** (from Microsoft Research) released with the paper [Deep Residual Learning for Image Recognition](https://arxiv.org/abs/1512.03385) by Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun.
|
||||
1. **[RoBERTa](https://huggingface.co/docs/transformers/model_doc/roberta)** (from Facebook), released together with the paper [RoBERTa: A Robustly Optimized BERT Pretraining Approach](https://arxiv.org/abs/1907.11692) by Yinhan Liu, Myle Ott, Naman Goyal, Jingfei Du, Mandar Joshi, Danqi Chen, Omer Levy, Mike Lewis, Luke Zettlemoyer, Veselin Stoyanov.
|
||||
1. **[RoFormer](https://huggingface.co/docs/transformers/model_doc/roformer)** (from ZhuiyiTechnology), released together with the paper [RoFormer: Enhanced Transformer with Rotary Position Embedding](https://arxiv.org/abs/2104.09864) by Jianlin Su and Yu Lu and Shengfeng Pan and Bo Wen and Yunfeng Liu.
|
||||
1. **[SegFormer](https://huggingface.co/docs/transformers/model_doc/segformer)** (from NVIDIA) released with the paper [SegFormer: Simple and Efficient Design for Semantic Segmentation with Transformers](https://arxiv.org/abs/2105.15203) by Enze Xie, Wenhai Wang, Zhiding Yu, Anima Anandkumar, Jose M. Alvarez, Ping Luo.
|
||||
1. **[Segment Anything](https://huggingface.co/docs/transformers/model_doc/sam)** (from Meta AI) released with the paper [Segment Anything](https://arxiv.org/pdf/2304.02643v1.pdf) by Alexander Kirillov, Eric Mintun, Nikhila Ravi, Hanzi Mao, Chloe Rolland, Laura Gustafson, Tete Xiao, Spencer Whitehead, Alex Berg, Wan-Yen Lo, Piotr Dollar, Ross Girshick.
|
||||
1. **[SigLIP](https://huggingface.co/docs/transformers/main/model_doc/siglip)** (from Google AI) released with the paper [Sigmoid Loss for Language Image Pre-Training](https://arxiv.org/abs/2303.15343) by Xiaohua Zhai, Basil Mustafa, Alexander Kolesnikov, Lucas Beyer.
|
||||
1. **[SpeechT5](https://huggingface.co/docs/transformers/model_doc/speecht5)** (from Microsoft Research) released with the paper [SpeechT5: Unified-Modal Encoder-Decoder Pre-Training for Spoken Language Processing](https://arxiv.org/abs/2110.07205) by Junyi Ao, Rui Wang, Long Zhou, Chengyi Wang, Shuo Ren, Yu Wu, Shujie Liu, Tom Ko, Qing Li, Yu Zhang, Zhihua Wei, Yao Qian, Jinyu Li, Furu Wei.
|
||||
1. **[SqueezeBERT](https://huggingface.co/docs/transformers/model_doc/squeezebert)** (from Berkeley) released with the paper [SqueezeBERT: What can computer vision teach NLP about efficient neural networks?](https://arxiv.org/abs/2006.11316) by Forrest N. Iandola, Albert E. Shaw, Ravi Krishna, and Kurt W. Keutzer.
|
||||
1. **[StableLm](https://huggingface.co/docs/transformers/model_doc/stablelm)** (from Stability AI) released with the paper [StableLM 3B 4E1T (Technical Report)](https://stability.wandb.io/stability-llm/stable-lm/reports/StableLM-3B-4E1T--VmlldzoyMjU4?accessToken=u3zujipenkx5g7rtcj9qojjgxpconyjktjkli2po09nffrffdhhchq045vp0wyfo) by Jonathan Tow, Marco Bellagente, Dakota Mahan, Carlos Riquelme Ruiz, Duy Phung, Maksym Zhuravinskyi, Nathan Cooper, Nikhil Pinnaparaju, Reshinth Adithyan, and James Baicoianu.
|
||||
1. **[Starcoder2](https://huggingface.co/docs/transformers/main/model_doc/starcoder2)** (from BigCode team) released with the paper [StarCoder 2 and The Stack v2: The Next Generation](https://arxiv.org/abs/2402.19173) by Anton Lozhkov, Raymond Li, Loubna Ben Allal, Federico Cassano, Joel Lamy-Poirier, Nouamane Tazi, Ao Tang, Dmytro Pykhtar, Jiawei Liu, Yuxiang Wei, Tianyang Liu, Max Tian, Denis Kocetkov, Arthur Zucker, Younes Belkada, Zijian Wang, Qian Liu, Dmitry Abulkhanov, Indraneil Paul, Zhuang Li, Wen-Ding Li, Megan Risdal, Jia Li, Jian Zhu, Terry Yue Zhuo, Evgenii Zheltonozhskii, Nii Osae Osae Dade, Wenhao Yu, Lucas Krauß, Naman Jain, Yixuan Su, Xuanli He, Manan Dey, Edoardo Abati, Yekun Chai, Niklas Muennighoff, Xiangru Tang, Muhtasham Oblokulov, Christopher Akiki, Marc Marone, Chenghao Mou, Mayank Mishra, Alex Gu, Binyuan Hui, Tri Dao, Armel Zebaze, Olivier Dehaene, Nicolas Patry, Canwen Xu, Julian McAuley, Han Hu, Torsten Scholak, Sebastien Paquet, Jennifer Robinson, Carolyn Jane Anderson, Nicolas Chapados, Mostofa Patwary, Nima Tajbakhsh, Yacine Jernite, Carlos Muñoz Ferrandis, Lingming Zhang, Sean Hughes, Thomas Wolf, Arjun Guha, Leandro von Werra, and Harm de Vries.
|
||||
1. **[Swin Transformer](https://huggingface.co/docs/transformers/model_doc/swin)** (from Microsoft) released with the paper [Swin Transformer: Hierarchical Vision Transformer using Shifted Windows](https://arxiv.org/abs/2103.14030) by Ze Liu, Yutong Lin, Yue Cao, Han Hu, Yixuan Wei, Zheng Zhang, Stephen Lin, Baining Guo.
|
||||
1. **[Swin2SR](https://huggingface.co/docs/transformers/model_doc/swin2sr)** (from University of Würzburg) released with the paper [Swin2SR: SwinV2 Transformer for Compressed Image Super-Resolution and Restoration](https://arxiv.org/abs/2209.11345) by Marcos V. Conde, Ui-Jin Choi, Maxime Burchi, Radu Timofte.
|
||||
1. **[T5](https://huggingface.co/docs/transformers/model_doc/t5)** (from Google AI) released with the paper [Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer](https://arxiv.org/abs/1910.10683) by Colin Raffel and Noam Shazeer and Adam Roberts and Katherine Lee and Sharan Narang and Michael Matena and Yanqi Zhou and Wei Li and Peter J. Liu.
|
||||
1. **[T5v1.1](https://huggingface.co/docs/transformers/model_doc/t5v1.1)** (from Google AI) released in the repository [google-research/text-to-text-transfer-transformer](https://github.com/google-research/text-to-text-transfer-transformer/blob/main/released_checkpoints.md#t511) by Colin Raffel and Noam Shazeer and Adam Roberts and Katherine Lee and Sharan Narang and Michael Matena and Yanqi Zhou and Wei Li and Peter J. Liu.
|
||||
1. **[Table Transformer](https://huggingface.co/docs/transformers/model_doc/table-transformer)** (from Microsoft Research) released with the paper [PubTables-1M: Towards Comprehensive Table Extraction From Unstructured Documents](https://arxiv.org/abs/2110.00061) by Brandon Smock, Rohith Pesala, Robin Abraham.
|
||||
1. **[TrOCR](https://huggingface.co/docs/transformers/model_doc/trocr)** (from Microsoft), released together with the paper [TrOCR: Transformer-based Optical Character Recognition with Pre-trained Models](https://arxiv.org/abs/2109.10282) by Minghao Li, Tengchao Lv, Lei Cui, Yijuan Lu, Dinei Florencio, Cha Zhang, Zhoujun Li, Furu Wei.
|
||||
1. **[UniSpeech](https://huggingface.co/docs/transformers/model_doc/unispeech)** (from Microsoft Research) released with the paper [UniSpeech: Unified Speech Representation Learning with Labeled and Unlabeled Data](https://arxiv.org/abs/2101.07597) by Chengyi Wang, Yu Wu, Yao Qian, Kenichi Kumatani, Shujie Liu, Furu Wei, Michael Zeng, Xuedong Huang.
|
||||
1. **[UniSpeechSat](https://huggingface.co/docs/transformers/model_doc/unispeech-sat)** (from Microsoft Research) released with the paper [UNISPEECH-SAT: UNIVERSAL SPEECH REPRESENTATION LEARNING WITH SPEAKER AWARE PRE-TRAINING](https://arxiv.org/abs/2110.05752) by Sanyuan Chen, Yu Wu, Chengyi Wang, Zhengyang Chen, Zhuo Chen, Shujie Liu, Jian Wu, Yao Qian, Furu Wei, Jinyu Li, Xiangzhan Yu.
|
||||
1. **[Vision Transformer (ViT)](https://huggingface.co/docs/transformers/model_doc/vit)** (from Google AI) released with the paper [An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale](https://arxiv.org/abs/2010.11929) by Alexey Dosovitskiy, Lucas Beyer, Alexander Kolesnikov, Dirk Weissenborn, Xiaohua Zhai, Thomas Unterthiner, Mostafa Dehghani, Matthias Minderer, Georg Heigold, Sylvain Gelly, Jakob Uszkoreit, Neil Houlsby.
|
||||
1. **[ViTMatte](https://huggingface.co/docs/transformers/model_doc/vitmatte)** (from HUST-VL) released with the paper [ViTMatte: Boosting Image Matting with Pretrained Plain Vision Transformers](https://arxiv.org/abs/2305.15272) by Jingfeng Yao, Xinggang Wang, Shusheng Yang, Baoyuan Wang.
|
||||
1. **[VITS](https://huggingface.co/docs/transformers/model_doc/vits)** (from Kakao Enterprise) released with the paper [Conditional Variational Autoencoder with Adversarial Learning for End-to-End Text-to-Speech](https://arxiv.org/abs/2106.06103) by Jaehyeon Kim, Jungil Kong, Juhee Son.
|
||||
1. **[Wav2Vec2](https://huggingface.co/docs/transformers/model_doc/wav2vec2)** (from Facebook AI) released with the paper [wav2vec 2.0: A Framework for Self-Supervised Learning of Speech Representations](https://arxiv.org/abs/2006.11477) by Alexei Baevski, Henry Zhou, Abdelrahman Mohamed, Michael Auli.
|
||||
1. **[Wav2Vec2-BERT](https://huggingface.co/docs/transformers/main/model_doc/wav2vec2-bert)** (from Meta AI) released with the paper [Seamless: Multilingual Expressive and Streaming Speech Translation](https://ai.meta.com/research/publications/seamless-multilingual-expressive-and-streaming-speech-translation/) by the Seamless Communication team.
|
||||
1. **[WavLM](https://huggingface.co/docs/transformers/model_doc/wavlm)** (from Microsoft Research) released with the paper [WavLM: Large-Scale Self-Supervised Pre-Training for Full Stack Speech Processing](https://arxiv.org/abs/2110.13900) by Sanyuan Chen, Chengyi Wang, Zhengyang Chen, Yu Wu, Shujie Liu, Zhuo Chen, Jinyu Li, Naoyuki Kanda, Takuya Yoshioka, Xiong Xiao, Jian Wu, Long Zhou, Shuo Ren, Yanmin Qian, Yao Qian, Jian Wu, Michael Zeng, Furu Wei.
|
||||
1. **[Whisper](https://huggingface.co/docs/transformers/model_doc/whisper)** (from OpenAI) released with the paper [Robust Speech Recognition via Large-Scale Weak Supervision](https://cdn.openai.com/papers/whisper.pdf) by Alec Radford, Jong Wook Kim, Tao Xu, Greg Brockman, Christine McLeavey, Ilya Sutskever.
|
||||
1. **[XLM](https://huggingface.co/docs/transformers/model_doc/xlm)** (from Facebook) released together with the paper [Cross-lingual Language Model Pretraining](https://arxiv.org/abs/1901.07291) by Guillaume Lample and Alexis Conneau.
|
||||
1. **[XLM-RoBERTa](https://huggingface.co/docs/transformers/model_doc/xlm-roberta)** (from Facebook AI), released together with the paper [Unsupervised Cross-lingual Representation Learning at Scale](https://arxiv.org/abs/1911.02116) by Alexis Conneau*, Kartikay Khandelwal*, Naman Goyal, Vishrav Chaudhary, Guillaume Wenzek, Francisco Guzmán, Edouard Grave, Myle Ott, Luke Zettlemoyer and Veselin Stoyanov.
|
||||
1. **[YOLOS](https://huggingface.co/docs/transformers/model_doc/yolos)** (from Huazhong University of Science & Technology) released with the paper [You Only Look at One Sequence: Rethinking Transformer in Vision through Object Detection](https://arxiv.org/abs/2106.00666) by Yuxin Fang, Bencheng Liao, Xinggang Wang, Jiemin Fang, Jiyang Qi, Rui Wu, Jianwei Niu, Wenyu Liu.
|
||||
@@ -1,70 +0,0 @@
|
||||
- local: index
|
||||
title: 🤗 Transformers.js
|
||||
- sections:
|
||||
- local: installation
|
||||
title: Installation
|
||||
- local: pipelines
|
||||
title: The pipeline API
|
||||
- local: custom_usage
|
||||
title: Custom usage
|
||||
title: Get started
|
||||
- sections:
|
||||
- local: tutorials/vanilla-js
|
||||
title: Building a Vanilla JS Application
|
||||
- local: tutorials/react
|
||||
title: Building a React Application
|
||||
- local: tutorials/next
|
||||
title: Building a Next.js Application
|
||||
- local: tutorials/browser-extension
|
||||
title: Building a Browser Extension
|
||||
- local: tutorials/electron
|
||||
title: Building an Electron Application
|
||||
- local: tutorials/node
|
||||
title: Server-side Inference in Node.js
|
||||
title: Tutorials
|
||||
- sections:
|
||||
- local: guides/private
|
||||
title: Accessing Private/Gated Models
|
||||
- local: guides/node-audio-processing
|
||||
title: Server-side Audio Processing in Node.js
|
||||
title: Developer Guides
|
||||
- sections:
|
||||
- local: api/transformers
|
||||
title: Index
|
||||
- local: api/pipelines
|
||||
title: Pipelines
|
||||
- local: api/models
|
||||
title: Models
|
||||
- local: api/tokenizers
|
||||
title: Tokenizers
|
||||
- local: api/processors
|
||||
title: Processors
|
||||
- local: api/configs
|
||||
title: Configs
|
||||
- local: api/env
|
||||
title: Environment variables
|
||||
- sections:
|
||||
- local: api/backends/onnx
|
||||
title: ONNX
|
||||
title: Backends
|
||||
isExpanded: false
|
||||
- sections:
|
||||
- local: api/utils/core
|
||||
title: Core
|
||||
- local: api/utils/hub
|
||||
title: Hub
|
||||
- local: api/utils/image
|
||||
title: Image
|
||||
- local: api/utils/audio
|
||||
title: Audio
|
||||
- local: api/utils/tensor
|
||||
title: Tensor
|
||||
- local: api/utils/maths
|
||||
title: Maths
|
||||
- local: api/utils/generation
|
||||
title: Generation
|
||||
- local: api/utils/data-structures
|
||||
title: Data Structures
|
||||
title: Utilities
|
||||
isExpanded: false
|
||||
title: API Reference
|
||||
@@ -1,3 +0,0 @@
|
||||
# These docs are automatically generated from the JSDoc in the source code.
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,7 +0,0 @@
|
||||
# Use custom models
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/4_custom-usage.snippet"
|
||||
}
|
||||
</include>
|
||||
@@ -1,111 +0,0 @@
|
||||
|
||||
# Server-side Audio Processing in Node.js
|
||||
|
||||
A major benefit of writing code for the web is that you can access the multitude of APIs that are available in modern browsers. Unfortunately, when writing server-side code, we are not afforded such luxury, so we have to find another way. In this tutorial, we will design a simple Node.js application that uses Transformers.js for speech recognition with [Whisper](https://huggingface.co/Xenova/whisper-tiny.en), and in the process, learn how to process audio on the server.
|
||||
|
||||
The main problem we need to solve is that the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) is not available in Node.js, meaning we can't use the [`AudioContext`](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext) class to process audio. So, we will need to install third-party libraries to obtain the raw audio data. For this example, we will only consider `.wav` files, but the same principles apply to other audio formats.
|
||||
|
||||
<Tip>
|
||||
|
||||
This tutorial will be written as an ES module, but you can easily adapt it to use CommonJS instead. For more information, see the [node tutorial](https://huggingface.co/docs/transformers.js/tutorials/node).
|
||||
|
||||
</Tip>
|
||||
|
||||
|
||||
**Useful links:**
|
||||
- [Source code](https://github.com/xenova/transformers.js/tree/main/examples/node-audio-processing)
|
||||
- [Documentation](https://huggingface.co/docs/transformers.js)
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) version 18+
|
||||
- [npm](https://www.npmjs.com/) version 9+
|
||||
|
||||
|
||||
|
||||
## Getting started
|
||||
|
||||
Let's start by creating a new Node.js project and installing Transformers.js via [NPM](https://www.npmjs.com/package/@xenova/transformers):
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm i @xenova/transformers
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
Remember to add `"type": "module"` to your `package.json` to indicate that your project uses ECMAScript modules.
|
||||
|
||||
</Tip>
|
||||
|
||||
|
||||
Next, let's install the [`wavefile`](https://www.npmjs.com/package/wavefile) package, which we will use for loading `.wav` files:
|
||||
|
||||
```bash
|
||||
npm i wavefile
|
||||
```
|
||||
|
||||
|
||||
## Creating the application
|
||||
|
||||
Start by creating a new file called `index.js`, which will be the entry point for our application. Let's also import the necessary modules:
|
||||
|
||||
```js
|
||||
import { pipeline } from '@xenova/transformers';
|
||||
import wavefile from 'wavefile';
|
||||
```
|
||||
|
||||
For this tutorial, we will use the `Xenova/whisper-tiny.en` model, but feel free to choose one of the other whisper models from the [Hugging Face Hub](https://huggingface.co/models?library=transformers.js&search=whisper). Let's create our pipeline with:
|
||||
```js
|
||||
let transcriber = await pipeline('automatic-speech-recognition', 'Xenova/whisper-tiny.en');
|
||||
```
|
||||
|
||||
Next, let's load an audio file and convert it to the format required by Transformers.js:
|
||||
```js
|
||||
// Load audio data
|
||||
let url = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/jfk.wav';
|
||||
let buffer = Buffer.from(await fetch(url).then(x => x.arrayBuffer()))
|
||||
|
||||
// Read .wav file and convert it to required format
|
||||
let wav = new wavefile.WaveFile(buffer);
|
||||
wav.toBitDepth('32f'); // Pipeline expects input as a Float32Array
|
||||
wav.toSampleRate(16000); // Whisper expects audio with a sampling rate of 16000
|
||||
let audioData = wav.getSamples();
|
||||
if (Array.isArray(audioData)) {
|
||||
if (audioData.length > 1) {
|
||||
const SCALING_FACTOR = Math.sqrt(2);
|
||||
|
||||
// Merge channels (into first channel to save memory)
|
||||
for (let i = 0; i < audioData[0].length; ++i) {
|
||||
audioData[0][i] = SCALING_FACTOR * (audioData[0][i] + audioData[1][i]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Select first channel
|
||||
audioData = audioData[0];
|
||||
}
|
||||
```
|
||||
|
||||
Finally, let's run the model and measure execution duration.
|
||||
```js
|
||||
let start = performance.now();
|
||||
let output = await transcriber(audioData);
|
||||
let end = performance.now();
|
||||
console.log(`Execution duration: ${(end - start) / 1000} seconds`);
|
||||
console.log(output);
|
||||
```
|
||||
|
||||
You can now run the application with `node index.js`. Note that when running the script for the first time, it may take a while to download and cache the model. Subsequent requests will use the cached model, and model loading will be much faster.
|
||||
|
||||
You should see output similar to:
|
||||
```
|
||||
Execution duration: 0.6460317999720574 seconds
|
||||
{
|
||||
text: ' And so my fellow Americans ask not what your country can do for you. Ask what you can do for your country.'
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
That's it! You've successfully created a Node.js application that uses Transformers.js for speech recognition with Whisper. You can now use this as a starting point for your own applications.
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
# Accessing Private/Gated Models
|
||||
|
||||
<Tip>
|
||||
|
||||
Due to the possibility of leaking access tokens to users of your website or web application, we only support accessing private/gated models from server-side environments (e.g., Node.js) that have access to the process' environment variables.
|
||||
|
||||
</Tip>
|
||||
|
||||
## Step 1: Generating a User Access Token
|
||||
|
||||
[User Access Tokens](https://huggingface.co/docs/hub/security-tokens) are the preferred way to authenticate an application to Hugging Face services.
|
||||
|
||||
To generate an access token, navigate to the [Access Tokens tab](https://huggingface.co/settings/tokens) in your settings and click on the **New token** button. Choose a name for your token and click **Generate a token** (we recommend keeping the "Role" as read-only). You can then click the **Copy** button next to your newly-created token to copy it to your clipboard.
|
||||
|
||||
<div class="flex justify-center">
|
||||
<img class="block dark:hidden" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/new-token.png"/>
|
||||
<img class="hidden dark:block" src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/hub/new-token-dark.png"/>
|
||||
</div>
|
||||
|
||||
To delete or refresh User Access Tokens, you can click the **Manage** button.
|
||||
|
||||
|
||||
## Step 2: Using the access token in Transformers.js
|
||||
|
||||
Transformers.js will attach an Authorization header to requests made to the Hugging Face Hub when the `HF_TOKEN` environment variable is set and visible to the process.
|
||||
|
||||
One way to do this is to call your program with the environment variable set. For example, let's say you have a file called `llama.js` with the following code:
|
||||
|
||||
```js
|
||||
import { AutoTokenizer } from '@xenova/transformers';
|
||||
|
||||
// Load tokenizer for a gated repository.
|
||||
const tokenizer = await AutoTokenizer.from_pretrained('meta-llama/Llama-2-7b-hf');
|
||||
|
||||
// Encode text.
|
||||
const text = 'Hello world!';
|
||||
const encoded = tokenizer.encode(text);
|
||||
console.log(encoded);
|
||||
```
|
||||
|
||||
You can then use the following command to set the `HF_TOKEN` environment variable and run the file:
|
||||
|
||||
```bash
|
||||
HF_TOKEN=hf_... node tests/llama.js
|
||||
```
|
||||
|
||||
(remember to replace `hf_...` with your actual access token).
|
||||
|
||||
If done correctly, you should see the following output:
|
||||
|
||||
```bash
|
||||
[ 1, 15043, 3186, 29991 ]
|
||||
```
|
||||
|
||||
|
||||
Alternatively, you can set the environment variable directly in your code:
|
||||
```js
|
||||
// Set access token (NB: Keep this private!)
|
||||
process.env.HF_TOKEN = 'hf_...';
|
||||
|
||||
// ... rest of your code
|
||||
```
|
||||
@@ -1,54 +0,0 @@
|
||||
# Transformers.js
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/0_introduction.snippet"
|
||||
}
|
||||
</include>
|
||||
|
||||
## Quick tour
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/1_quick-tour.snippet"
|
||||
}
|
||||
</include>
|
||||
|
||||
|
||||
## Contents
|
||||
|
||||
The documentation is organized into 4 sections:
|
||||
1. **GET STARTED** provides a quick tour of the library and installation instructions to get up and running.
|
||||
2. **TUTORIALS** are a great place to start if you're a beginner! We also include sample applications for you to play around with!
|
||||
3. **DEVELOPER GUIDES** show you how to use the library to achieve a specific goal.
|
||||
4. **API REFERENCE** describes all classes and functions, as well as their available parameters and types.
|
||||
|
||||
## Examples
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/3_examples.snippet"
|
||||
}
|
||||
</include>
|
||||
|
||||
## Supported tasks/models
|
||||
|
||||
Here is the list of all tasks and architectures currently supported by Transformers.js.
|
||||
If you don't see your task/model listed here or it is not yet supported, feel free
|
||||
to open up a feature request [here](https://github.com/xenova/transformers.js/issues/new/choose).
|
||||
|
||||
To find compatible models on the Hub, select the "transformers.js" library tag in the filter menu (or visit [this link](https://huggingface.co/models?library=transformers.js)).
|
||||
You can refine your search by selecting the task you're interested in (e.g., [text-classification](https://huggingface.co/models?pipeline_tag=text-classification&library=transformers.js)).
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/5_supported-tasks.snippet"
|
||||
}
|
||||
</include>
|
||||
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/6_supported-models.snippet"
|
||||
}
|
||||
</include>
|
||||
@@ -1,7 +0,0 @@
|
||||
# Installation
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/2_installation.snippet"
|
||||
}
|
||||
</include>
|
||||
@@ -1,161 +0,0 @@
|
||||
# The `pipeline` API
|
||||
|
||||
Just like the [transformers Python library](https://github.com/huggingface/transformers), Transformers.js provides users with a simple way to leverage the power of transformers. The `pipeline()` function is the easiest and fastest way to use a pretrained model for inference.
|
||||
|
||||
<Tip>
|
||||
|
||||
For the full list of available tasks/pipelines, check out [this table](#available-tasks).
|
||||
|
||||
</Tip>
|
||||
|
||||
|
||||
## The basics
|
||||
|
||||
Start by creating an instance of `pipeline()` and specifying a task you want to use it for. For example, to create a sentiment analysis pipeline, you can do:
|
||||
|
||||
```javascript
|
||||
import { pipeline } from '@xenova/transformers';
|
||||
|
||||
let classifier = await pipeline('sentiment-analysis');
|
||||
```
|
||||
|
||||
When running for the first time, the `pipeline` will download and cache the default pretrained model associated with the task. This can take a while, but subsequent calls will be much faster.
|
||||
|
||||
<Tip>
|
||||
|
||||
By default, models will be downloaded from the [Hugging Face Hub](https://huggingface.co/models) and stored in [browser cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache), but there are ways to specify custom models and cache locations. For more information see [here](./custom_usage).
|
||||
|
||||
</Tip>
|
||||
|
||||
You can now use the classifier on your target text by calling it as a function:
|
||||
|
||||
```javascript
|
||||
let result = await classifier('I love transformers!');
|
||||
// [{'label': 'POSITIVE', 'score': 0.9998}]
|
||||
```
|
||||
|
||||
If you have multiple inputs, you can pass them as an array:
|
||||
|
||||
```javascript
|
||||
let result = await classifier(['I love transformers!', 'I hate transformers!']);
|
||||
// [{'label': 'POSITIVE', 'score': 0.9998}, {'label': 'NEGATIVE', 'score': 0.9982}]
|
||||
```
|
||||
|
||||
You can also specify a different model to use for the pipeline by passing it as the second argument to the `pipeline()` function. For example, to use a different model for sentiment analysis (like one trained to predict sentiment of a review as a number of stars between 1 and 5), you can do:
|
||||
|
||||
<!-- TODO: REPLACE 'nlptown/bert-base-multilingual-uncased-sentiment' with 'nlptown/bert-base-multilingual-uncased-sentiment'-->
|
||||
|
||||
```javascript
|
||||
let reviewer = await pipeline('sentiment-analysis', 'Xenova/bert-base-multilingual-uncased-sentiment');
|
||||
|
||||
let result = await reviewer('The Shawshank Redemption is a true masterpiece of cinema.');
|
||||
// [{label: '5 stars', score: 0.8167929649353027}]
|
||||
```
|
||||
|
||||
Transformers.js supports loading any model hosted on the Hugging Face Hub, provided it has ONNX weights (located in a subfolder called `onnx`). For more information on how to convert your PyTorch, TensorFlow, or JAX model to ONNX, see the [conversion section](./custom_usage#convert-your-models-to-onnx).
|
||||
|
||||
The `pipeline()` function is a great way to quickly use a pretrained model for inference, as it takes care of all the preprocessing and postprocessing for you. For example, if you want to perform Automatic Speech Recognition (ASR) using OpenAI's Whisper model, you can do:
|
||||
|
||||
<!-- TODO: Replace 'Xenova/whisper-small.en' with 'openai/whisper-small.en' -->
|
||||
```javascript
|
||||
// Allocate a pipeline for Automatic Speech Recognition
|
||||
let transcriber = await pipeline('automatic-speech-recognition', 'Xenova/whisper-small.en');
|
||||
|
||||
// Transcribe an audio file, loaded from a URL.
|
||||
let result = await transcriber('https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac');
|
||||
// {text: ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'}
|
||||
```
|
||||
|
||||
## Pipeline options
|
||||
|
||||
### Loading
|
||||
|
||||
We offer a variety of options to control how models are loaded from the Hugging Face Hub (or locally).
|
||||
By default, the *quantized* version of the model is used, which is smaller and faster, but usually less accurate.
|
||||
To override this behaviour (i.e., use the unquantized model), you can use a custom `PretrainedOptions` object
|
||||
as the third parameter to the `pipeline` function:
|
||||
|
||||
```javascript
|
||||
// Allocation a pipeline for feature extraction, using the unquantized model
|
||||
const pipe = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {
|
||||
quantized: false,
|
||||
});
|
||||
```
|
||||
|
||||
You can also specify which revision of the model to use, by passing a `revision` parameter.
|
||||
Since the Hugging Face Hub uses a git-based versioning system, you can use any valid git revision specifier (e.g., branch name or commit hash)
|
||||
|
||||
```javascript
|
||||
let transcriber = await pipeline('automatic-speech-recognition', 'Xenova/whisper-tiny.en', {
|
||||
revision: 'output_attentions',
|
||||
});
|
||||
```
|
||||
|
||||
For the full list of options, check out the [PretrainedOptions](./api/utils/hub#module_utils/hub..PretrainedOptions) documentation.
|
||||
|
||||
|
||||
### Running
|
||||
Many pipelines have additional options that you can specify. For example, when using a model that does multilingual translation, you can specify the source and target languages like this:
|
||||
|
||||
<!-- TODO: Replace 'Xenova/nllb-200-distilled-600M' with 'facebook/nllb-200-distilled-600M' -->
|
||||
```javascript
|
||||
// Allocation a pipeline for translation
|
||||
let translator = await pipeline('translation', 'Xenova/nllb-200-distilled-600M');
|
||||
|
||||
// Translate from English to Greek
|
||||
let result = await translator('I like to walk my dog.', {
|
||||
src_lang: 'eng_Latn',
|
||||
tgt_lang: 'ell_Grek'
|
||||
});
|
||||
// [ { translation_text: 'Μου αρέσει να περπατάω το σκυλί μου.' } ]
|
||||
|
||||
// Translate back to English
|
||||
let result2 = await translator(result[0].translation_text, {
|
||||
src_lang: 'ell_Grek',
|
||||
tgt_lang: 'eng_Latn'
|
||||
});
|
||||
// [ { translation_text: 'I like to walk my dog.' } ]
|
||||
```
|
||||
|
||||
When using models that support auto-regressive generation, you can specify generation parameters like the number of new tokens, sampling methods, temperature, repetition penalty, and much more. For a full list of available parameters, see to the [GenerationConfig](./api/utils/generation#module_utils/generation.GenerationConfig) class.
|
||||
|
||||
For example, to generate a poem using `LaMini-Flan-T5-783M`, you can do:
|
||||
|
||||
<!-- TODO: Replace 'Xenova/LaMini-Flan-T5-783M' with 'MBZUAI/LaMini-Flan-T5-783M' -->
|
||||
|
||||
```javascript
|
||||
// Allocate a pipeline for text2text-generation
|
||||
let poet = await pipeline('text2text-generation', 'Xenova/LaMini-Flan-T5-783M');
|
||||
let result = await poet('Write me a love poem about cheese.', {
|
||||
max_new_tokens: 200,
|
||||
temperature: 0.9,
|
||||
repetition_penalty: 2.0,
|
||||
no_repeat_ngram_size: 3,
|
||||
});
|
||||
```
|
||||
|
||||
Logging `result[0].generated_text` to the console gives:
|
||||
|
||||
```
|
||||
Cheese, oh cheese! You're the perfect comfort food.
|
||||
Your texture so smooth and creamy you can never get old.
|
||||
With every bite it melts in your mouth like buttery delights
|
||||
that make me feel right at home with this sweet treat of mine.
|
||||
|
||||
From classic to bold flavor combinations,
|
||||
I love how versatile you are as an ingredient too?
|
||||
Cheddar is my go-to for any occasion or mood;
|
||||
It adds depth and richness without being overpowering its taste buds alone
|
||||
```
|
||||
|
||||
For more information on the available options for each pipeline, refer to the [API Reference](./api/pipelines).
|
||||
If you would like more control over the inference process, you can use the [`AutoModel`](./api/models), [`AutoTokenizer`](./api/tokenizers), or [`AutoProcessor`](./api/processors) classes instead.
|
||||
|
||||
|
||||
## Available tasks
|
||||
|
||||
<include>
|
||||
{
|
||||
"path": "../snippets/5_supported-tasks.snippet"
|
||||
}
|
||||
</include>
|
||||
@@ -1,4 +0,0 @@
|
||||
# Building a browser extension
|
||||
|
||||
*Full tutorial coming soon...* In the meantime, check out the example application: https://github.com/xenova/transformers.js/tree/main/examples/extension
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Building an Electron application
|
||||
|
||||
*Full tutorial coming soon...* In the meantime, check out the example application: https://github.com/xenova/transformers.js/tree/main/examples/electron
|
||||
@@ -1,433 +0,0 @@
|
||||
|
||||
# Building a Next.js application
|
||||
|
||||
In this tutorial, we'll build a simple Next.js application that performs sentiment analysis using Transformers.js!
|
||||
Since Transformers.js can run in the browser or in Node.js, you can choose whether you want to perform inference [client-side](#client-side-inference) or [server-side](#server-side-inference) (we'll show you how to do both). In either case, we will be developing with the new [App Router](https://nextjs.org/docs/app) paradigm.
|
||||
The final product will look something like this:
|
||||
|
||||

|
||||
|
||||
Useful links:
|
||||
- Demo site: [client-side](https://huggingface.co/spaces/Xenova/next-example-app) or [server-side](https://huggingface.co/spaces/Xenova/next-server-example-app)
|
||||
- Source code: [client-side](https://github.com/xenova/transformers.js/tree/main/examples/next-client) or [server-side](https://github.com/xenova/transformers.js/tree/main/examples/next-server)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) version 18+
|
||||
- [npm](https://www.npmjs.com/) version 9+
|
||||
|
||||
## Client-side inference
|
||||
|
||||
|
||||
### Step 1: Initialise the project
|
||||
|
||||
Start by creating a new Next.js application using `create-next-app`:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest
|
||||
```
|
||||
|
||||
On installation, you'll see various prompts. For this demo, we'll be selecting those shown below in bold:
|
||||
|
||||
<pre>√ What is your project named? ... next
|
||||
√ Would you like to use TypeScript? ... <b>No</b> / Yes
|
||||
√ Would you like to use ESLint? ... No / <b>Yes</b>
|
||||
√ Would you like to use Tailwind CSS? ... No / <b>Yes</b>
|
||||
√ Would you like to use `src/` directory? ... No / <b>Yes</b>
|
||||
√ Would you like to use App Router? (recommended) ... No / <b>Yes</b>
|
||||
√ Would you like to customize the default import alias? ... <b>No</b> / Yes
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
### Step 2: Install and configure Transformers.js
|
||||
|
||||
You can install Transformers.js from [NPM](https://www.npmjs.com/package/@xenova/transformers) with the following command:
|
||||
|
||||
|
||||
```bash
|
||||
npm i @xenova/transformers
|
||||
```
|
||||
|
||||
We also need to update the `next.config.js` file to ignore node-specific modules when bundling for the browser:
|
||||
|
||||
```js
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// (Optional) Export as a static site
|
||||
// See https://nextjs.org/docs/pages/building-your-application/deploying/static-exports#configuration
|
||||
output: 'export', // Feel free to modify/remove this option
|
||||
|
||||
// Override the default webpack configuration
|
||||
webpack: (config) => {
|
||||
// See https://webpack.js.org/configuration/resolve/#resolvealias
|
||||
config.resolve.alias = {
|
||||
...config.resolve.alias,
|
||||
"sharp$": false,
|
||||
"onnxruntime-node$": false,
|
||||
}
|
||||
return config;
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
```
|
||||
|
||||
Next, we'll create a new [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) script where we'll place all ML-related code. This is to ensure that the main thread is not blocked while the model is loading and performing inference. For this application, we'll be using [`Xenova/distilbert-base-uncased-finetuned-sst-2-english`](https://huggingface.co/Xenova/distilbert-base-uncased-finetuned-sst-2-english), a ~67M parameter model finetuned on the [Stanford Sentiment Treebank](https://huggingface.co/datasets/sst) dataset. Add the following code to `./src/app/worker.js`:
|
||||
|
||||
```js
|
||||
import { pipeline, env } from "@xenova/transformers";
|
||||
|
||||
// Skip local model check
|
||||
env.allowLocalModels = false;
|
||||
|
||||
// Use the Singleton pattern to enable lazy construction of the pipeline.
|
||||
class PipelineSingleton {
|
||||
static task = 'text-classification';
|
||||
static model = 'Xenova/distilbert-base-uncased-finetuned-sst-2-english';
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
this.instance = pipeline(this.task, this.model, { progress_callback });
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for messages from the main thread
|
||||
self.addEventListener('message', async (event) => {
|
||||
// Retrieve the classification pipeline. When called for the first time,
|
||||
// this will load the pipeline and save it for future use.
|
||||
let classifier = await PipelineSingleton.getInstance(x => {
|
||||
// We also add a progress callback to the pipeline so that we can
|
||||
// track model loading.
|
||||
self.postMessage(x);
|
||||
});
|
||||
|
||||
// Actually perform the classification
|
||||
let output = await classifier(event.data.text);
|
||||
|
||||
// Send the output back to the main thread
|
||||
self.postMessage({
|
||||
status: 'complete',
|
||||
output: output,
|
||||
});
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Step 3: Design the user interface
|
||||
|
||||
We'll now modify the default `./src/app/page.js` file so that it connects to our worker thread. Since we'll only be performing in-browser inference, we can opt-in to Client components using the [`'use client'` directive](https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive).
|
||||
|
||||
```jsx
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect, useRef, useCallback } from 'react'
|
||||
|
||||
export default function Home() {
|
||||
/* TODO: Add state variables */
|
||||
|
||||
// Create a reference to the worker object.
|
||||
const worker = useRef(null);
|
||||
|
||||
// We use the `useEffect` hook to set up the worker as soon as the `App` component is mounted.
|
||||
useEffect(() => {
|
||||
if (!worker.current) {
|
||||
// Create the worker if it does not yet exist.
|
||||
worker.current = new Worker(new URL('./worker.js', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
}
|
||||
|
||||
// Create a callback function for messages from the worker thread.
|
||||
const onMessageReceived = (e) => { /* TODO: See below */};
|
||||
|
||||
// Attach the callback function as an event listener.
|
||||
worker.current.addEventListener('message', onMessageReceived);
|
||||
|
||||
// Define a cleanup function for when the component is unmounted.
|
||||
return () => worker.current.removeEventListener('message', onMessageReceived);
|
||||
});
|
||||
|
||||
const classify = useCallback((text) => {
|
||||
if (worker.current) {
|
||||
worker.current.postMessage({ text });
|
||||
}
|
||||
}, []);
|
||||
|
||||
return ( /* TODO: See below */ )
|
||||
}
|
||||
```
|
||||
|
||||
Initialise the following state variables at the beginning of the `Home` component:
|
||||
```jsx
|
||||
// Keep track of the classification result and the model loading status.
|
||||
const [result, setResult] = useState(null);
|
||||
const [ready, setReady] = useState(null);
|
||||
```
|
||||
|
||||
and fill in the `onMessageReceived` function to update these variables when the worker thread sends a message:
|
||||
|
||||
```js
|
||||
const onMessageReceived = (e) => {
|
||||
switch (e.data.status) {
|
||||
case 'initiate':
|
||||
setReady(false);
|
||||
break;
|
||||
case 'ready':
|
||||
setReady(true);
|
||||
break;
|
||||
case 'complete':
|
||||
setResult(e.data.output[0])
|
||||
break;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Finally, we can add a simple UI to the `Home` component, consisting of an input textbox and a preformatted text element to display the classification result:
|
||||
```jsx
|
||||
<main className="flex min-h-screen flex-col items-center justify-center p-12">
|
||||
<h1 className="text-5xl font-bold mb-2 text-center">Transformers.js</h1>
|
||||
<h2 className="text-2xl mb-4 text-center">Next.js template</h2>
|
||||
|
||||
<input
|
||||
className="w-full max-w-xs p-2 border border-gray-300 rounded mb-4"
|
||||
type="text"
|
||||
placeholder="Enter text here"
|
||||
onInput={e => {
|
||||
classify(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
{ready !== null && (
|
||||
<pre className="bg-gray-100 p-2 rounded">
|
||||
{ (!ready || !result) ? 'Loading...' : JSON.stringify(result, null, 2) }
|
||||
</pre>
|
||||
)}
|
||||
</main>
|
||||
```
|
||||
|
||||
You can now run your application using the following command:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Visit the URL shown in the terminal (e.g., [http://localhost:3000/](http://localhost:3000/)) to see your application in action!
|
||||
|
||||
### (Optional) Step 4: Build and deploy
|
||||
|
||||
To build your application, simply run:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
This will bundle your application and output the static files to the `out` folder.
|
||||
|
||||
For this demo, we will deploy our application as a static [Hugging Face Space](https://huggingface.co/docs/hub/spaces), but you can deploy it anywhere you like! If you haven't already, you can create a free Hugging Face account [here](https://huggingface.co/join).
|
||||
|
||||
1. Visit [https://huggingface.co/new-space](https://huggingface.co/new-space) and fill in the form. Remember to select "Static" as the space type.
|
||||
2. Click the "Create space" button at the bottom of the page.
|
||||
3. Go to "Files" → "Add file" → "Upload files". Drag the files from the `out` folder into the upload box and click "Upload". After they have uploaded, scroll down to the button and click "Commit changes to main".
|
||||
|
||||
**That's it!** Your application should now be live at `https://huggingface.co/spaces/<your-username>/<your-space-name>`!
|
||||
|
||||
|
||||
## Server-side inference
|
||||
|
||||
While there are many different ways to perform server-side inference, the simplest (which we will discuss in this tutorial) is using the new [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) feature.
|
||||
|
||||
|
||||
### Step 1: Initialise the project
|
||||
|
||||
Start by creating a new Next.js application using `create-next-app`:
|
||||
|
||||
```bash
|
||||
npx create-next-app@latest
|
||||
```
|
||||
|
||||
On installation, you'll see various prompts. For this demo, we'll be selecting those shown below in bold:
|
||||
|
||||
<pre>√ What is your project named? ... next
|
||||
√ Would you like to use TypeScript? ... <b>No</b> / Yes
|
||||
√ Would you like to use ESLint? ... No / <b>Yes</b>
|
||||
√ Would you like to use Tailwind CSS? ... No / <b>Yes</b>
|
||||
√ Would you like to use `src/` directory? ... No / <b>Yes</b>
|
||||
√ Would you like to use App Router? (recommended) ... No / <b>Yes</b>
|
||||
√ Would you like to customize the default import alias? ... <b>No</b> / Yes
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
### Step 2: Install and configure Transformers.js
|
||||
|
||||
You can install Transformers.js from [NPM](https://www.npmjs.com/package/@xenova/transformers) with the following command:
|
||||
|
||||
|
||||
```bash
|
||||
npm i @xenova/transformers
|
||||
```
|
||||
|
||||
We also need to update the `next.config.js` file to prevent Webpack from bundling certain packages:
|
||||
|
||||
```js
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// (Optional) Export as a standalone site
|
||||
// See https://nextjs.org/docs/pages/api-reference/next-config-js/output#automatically-copying-traced-files
|
||||
output: 'standalone', // Feel free to modify/remove this option
|
||||
|
||||
// Indicate that these packages should not be bundled by webpack
|
||||
experimental: {
|
||||
serverComponentsExternalPackages: ['sharp', 'onnxruntime-node'],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig
|
||||
```
|
||||
|
||||
Next, let's set up our Route Handler. We can do this by creating two files in a new `./src/app/classify/` directory:
|
||||
|
||||
1. `pipeline.js` - to handle the construction of our pipeline.
|
||||
|
||||
```js
|
||||
import { pipeline } from "@xenova/transformers";
|
||||
|
||||
// Use the Singleton pattern to enable lazy construction of the pipeline.
|
||||
// NOTE: We wrap the class in a function to prevent code duplication (see below).
|
||||
const P = () => class PipelineSingleton {
|
||||
static task = 'text-classification';
|
||||
static model = 'Xenova/distilbert-base-uncased-finetuned-sst-2-english';
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
this.instance = pipeline(this.task, this.model, { progress_callback });
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
let PipelineSingleton;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// When running in development mode, attach the pipeline to the
|
||||
// global object so that it's preserved between hot reloads.
|
||||
// For more information, see https://vercel.com/guides/nextjs-prisma-postgres
|
||||
if (!global.PipelineSingleton) {
|
||||
global.PipelineSingleton = P();
|
||||
}
|
||||
PipelineSingleton = global.PipelineSingleton;
|
||||
} else {
|
||||
PipelineSingleton = P();
|
||||
}
|
||||
export default PipelineSingleton;
|
||||
```
|
||||
|
||||
2. `route.js` - to process requests made to the `/classify` route.
|
||||
```js
|
||||
import { NextResponse } from 'next/server'
|
||||
import PipelineSingleton from './pipeline.js';
|
||||
|
||||
export async function GET(request) {
|
||||
const text = request.nextUrl.searchParams.get('text');
|
||||
if (!text) {
|
||||
return NextResponse.json({
|
||||
error: 'Missing text parameter',
|
||||
}, { status: 400 });
|
||||
}
|
||||
// Get the classification pipeline. When called for the first time,
|
||||
// this will load the pipeline and cache it for future use.
|
||||
const classifier = await PipelineSingleton.getInstance();
|
||||
|
||||
// Actually perform the classification
|
||||
const result = await classifier(text);
|
||||
|
||||
return NextResponse.json(result);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Design the user interface
|
||||
|
||||
We'll now modify the default `./src/app/page.js` file to make requests to our newly-created Route Handler.
|
||||
|
||||
```jsx
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function Home() {
|
||||
|
||||
// Keep track of the classification result and the model loading status.
|
||||
const [result, setResult] = useState(null);
|
||||
const [ready, setReady] = useState(null);
|
||||
|
||||
const classify = async (text) => {
|
||||
if (!text) return;
|
||||
if (ready === null) setReady(false);
|
||||
|
||||
// Make a request to the /classify route on the server.
|
||||
const result = await fetch(`/classify?text=${encodeURIComponent(text)}`);
|
||||
|
||||
// If this is the first time we've made a request, set the ready flag.
|
||||
if (!ready) setReady(true);
|
||||
|
||||
const json = await result.json();
|
||||
setResult(json);
|
||||
};
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center p-12">
|
||||
<h1 className="text-5xl font-bold mb-2 text-center">Transformers.js</h1>
|
||||
<h2 className="text-2xl mb-4 text-center">Next.js template (server-side)</h2>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full max-w-xs p-2 border border-gray-300 rounded mb-4"
|
||||
placeholder="Enter text here"
|
||||
onInput={e => {
|
||||
classify(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
{ready !== null && (
|
||||
<pre className="bg-gray-100 p-2 rounded">
|
||||
{
|
||||
(!ready || !result) ? 'Loading...' : JSON.stringify(result, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
You can now run your application using the following command:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Visit the URL shown in the terminal (e.g., [http://localhost:3000/](http://localhost:3000/)) to see your application in action!
|
||||
|
||||
### (Optional) Step 4: Build and deploy
|
||||
|
||||
For this demo, we will build and deploy our application to [Hugging Face Spaces](https://huggingface.co/docs/hub/spaces). If you haven't already, you can create a free Hugging Face account [here](https://huggingface.co/join).
|
||||
|
||||
1. Create a new `Dockerfile` in your project's root folder. You can use our [example Dockerfile](https://github.com/xenova/transformers.js/blob/main/examples/next-server/Dockerfile) as a template.
|
||||
2. Visit [https://huggingface.co/new-space](https://huggingface.co/new-space) and fill in the form. Remember to select "Docker" as the space type (you can choose the "Blank" Docker template).
|
||||
3. Click the "Create space" button at the bottom of the page.
|
||||
4. Go to "Files" → "Add file" → "Upload files". Drag the files from your project folder (excluding `node_modules` and `.next`, if present) into the upload box and click "Upload". After they have uploaded, scroll down to the button and click "Commit changes to main".
|
||||
5. Add the following lines to the top of your `README.md`:
|
||||
```
|
||||
---
|
||||
title: Next Server Example App
|
||||
emoji: 🔥
|
||||
colorFrom: yellow
|
||||
colorTo: red
|
||||
sdk: docker
|
||||
pinned: false
|
||||
app_port: 3000
|
||||
---
|
||||
```
|
||||
|
||||
**That's it!** Your application should now be live at `https://huggingface.co/spaces/<your-username>/<your-space-name>`!
|
||||
@@ -1,218 +0,0 @@
|
||||
|
||||
# Server-side Inference in Node.js
|
||||
|
||||
Although Transformers.js was originally designed to be used in the browser, it's also able to run inference on the server. In this tutorial, we will design a simple Node.js API that uses Transformers.js for sentiment analysis.
|
||||
|
||||
We'll also show you how to use the library in both CommonJS and ECMAScript modules, so you can choose the module system that works best for your project:
|
||||
|
||||
- [ECMAScript modules (ESM)](#ecmascript-modules-esm) - The official standard format
|
||||
to package JavaScript code for reuse. It's the default module system in modern
|
||||
browsers, with modules imported using `import` and exported using `export`.
|
||||
Fortunately, starting with version 13.2.0, Node.js has stable support of ES modules.
|
||||
- [CommonJS](#commonjs) - The default module system in Node.js. In this system,
|
||||
modules are imported using `require()` and exported using `module.exports`.
|
||||
|
||||
<Tip>
|
||||
|
||||
Although you can always use the [Python library](https://github.com/huggingface/transformers) for server-side inference, using Transformers.js means that you can write all of your code in JavaScript (instead of having to set up and communicate with a separate Python process).
|
||||
|
||||
</Tip>
|
||||
|
||||
**Useful links:**
|
||||
- Source code ([ESM](https://github.com/xenova/transformers.js/tree/main/examples/node/esm/app.js) or [CommonJS](https://github.com/xenova/transformers.js/tree/main/examples/node/commonjs/app.js))
|
||||
- [Documentation](https://huggingface.co/docs/transformers.js)
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) version 18+
|
||||
- [npm](https://www.npmjs.com/) version 9+
|
||||
|
||||
|
||||
## Getting started
|
||||
|
||||
Let's start by creating a new Node.js project and installing Transformers.js via [NPM](https://www.npmjs.com/package/@xenova/transformers):
|
||||
|
||||
```bash
|
||||
npm init -y
|
||||
npm i @xenova/transformers
|
||||
```
|
||||
|
||||
Next, create a new file called `app.js`, which will be the entry point for our application. Depending on whether you're using [ECMAScript modules](#ecmascript-modules-esm) or [CommonJS](#commonjs), you will need to do some things differently (see below).
|
||||
|
||||
We'll also create a helper class called `MyClassificationPipeline` control the loading of the pipeline. It uses the [singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) to lazily create a single instance of the pipeline when `getInstance` is first called, and uses this pipeline for all subsequent calls:
|
||||
|
||||
|
||||
### ECMAScript modules (ESM)
|
||||
|
||||
To indicate that your project uses ECMAScript modules, you need to add `"type": "module"` to your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
...
|
||||
"type": "module",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Next, you will need to add the following imports to the top of `app.js`:
|
||||
|
||||
```javascript
|
||||
import http from 'http';
|
||||
import querystring from 'querystring';
|
||||
import url from 'url';
|
||||
```
|
||||
|
||||
Following that, let's import Transformers.js and define the `MyClassificationPipeline` class.
|
||||
|
||||
```javascript
|
||||
import { pipeline, env } from '@xenova/transformers';
|
||||
|
||||
class MyClassificationPipeline {
|
||||
static task = 'text-classification';
|
||||
static model = 'Xenova/distilbert-base-uncased-finetuned-sst-2-english';
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
// NOTE: Uncomment this to change the cache directory
|
||||
// env.cacheDir = './.cache';
|
||||
|
||||
this.instance = pipeline(this.task, this.model, { progress_callback });
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CommonJS
|
||||
|
||||
Start by adding the following imports to the top of `app.js`:
|
||||
|
||||
```javascript
|
||||
const http = require('http');
|
||||
const querystring = require('querystring');
|
||||
const url = require('url');
|
||||
```
|
||||
|
||||
Following that, let's import Transformers.js and define the `MyClassificationPipeline` class. Since Transformers.js is an ESM module, we will need to dynamically import the library using the [`import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) function:
|
||||
|
||||
```javascript
|
||||
class MyClassificationPipeline {
|
||||
static task = 'text-classification';
|
||||
static model = 'Xenova/distilbert-base-uncased-finetuned-sst-2-english';
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
// Dynamically import the Transformers.js library
|
||||
let { pipeline, env } = await import('@xenova/transformers');
|
||||
|
||||
// NOTE: Uncomment this to change the cache directory
|
||||
// env.cacheDir = './.cache';
|
||||
|
||||
this.instance = pipeline(this.task, this.model, { progress_callback });
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating a basic HTTP server
|
||||
|
||||
Next, let's create a basic server with the built-in [HTTP](https://nodejs.org/api/http.html#http) module. We will listen for requests made to the server (using the `/classify` endpoint), extract the `text` query parameter, and run this through the pipeline.
|
||||
|
||||
```javascript
|
||||
// Define the HTTP server
|
||||
const server = http.createServer();
|
||||
const hostname = '127.0.0.1';
|
||||
const port = 3000;
|
||||
|
||||
// Listen for requests made to the server
|
||||
server.on('request', async (req, res) => {
|
||||
// Parse the request URL
|
||||
const parsedUrl = url.parse(req.url);
|
||||
|
||||
// Extract the query parameters
|
||||
const { text } = querystring.parse(parsedUrl.query);
|
||||
|
||||
// Set the response headers
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
|
||||
let response;
|
||||
if (parsedUrl.pathname === '/classify' && text) {
|
||||
const classifier = await MyClassificationPipeline.getInstance();
|
||||
response = await classifier(text);
|
||||
res.statusCode = 200;
|
||||
} else {
|
||||
response = { 'error': 'Bad request' }
|
||||
res.statusCode = 400;
|
||||
}
|
||||
|
||||
// Send the JSON response
|
||||
res.end(JSON.stringify(response));
|
||||
});
|
||||
|
||||
server.listen(port, hostname, () => {
|
||||
console.log(`Server running at http://${hostname}:${port}/`);
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
Since we use lazy loading, the first request made to the server will also be responsible for loading the pipeline. If you would like to begin loading the pipeline as soon as the server starts running, you can add the following line of code after defining `MyClassificationPipeline`:
|
||||
|
||||
```javascript
|
||||
MyClassificationPipeline.getInstance();
|
||||
```
|
||||
|
||||
</Tip>
|
||||
|
||||
To start the server, run the following command:
|
||||
|
||||
```bash
|
||||
node app.js
|
||||
```
|
||||
|
||||
The server should be live at http://127.0.0.1:3000/, which you can visit in your web browser. You should see the following message:
|
||||
|
||||
```json
|
||||
{"error":"Bad request"}
|
||||
```
|
||||
|
||||
This is because we aren't targeting the `/classify` endpoint with a valid `text` query parameter. Let's try again, this time with a valid request. For example, you can visit http://127.0.0.1:3000/classify?text=I%20love%20Transformers.js and you should see:
|
||||
|
||||
```json
|
||||
[{"label":"POSITIVE","score":0.9996721148490906}]
|
||||
```
|
||||
|
||||
Great! We've successfully created a basic HTTP server that uses Transformers.js to classify text.
|
||||
|
||||
## (Optional) Customization
|
||||
|
||||
### Model caching
|
||||
|
||||
By default, the first time you run the application, it will download the model files and cache them on your file system (in `./node_modules/@xenova/transformers/.cache/`). All subsequent requests will then use this model. You can change the location of the cache by setting `env.cacheDir`. For example, to cache the model in the `.cache` directory in the current working directory, you can add:
|
||||
|
||||
```javascript
|
||||
env.cacheDir = './.cache';
|
||||
```
|
||||
|
||||
### Use local models
|
||||
|
||||
If you want to use local model files, you can set `env.localModelPath` as follows:
|
||||
|
||||
```javascript
|
||||
// Specify a custom location for models (defaults to '/models/').
|
||||
env.localModelPath = '/path/to/models/';
|
||||
```
|
||||
|
||||
You can also disable loading of remote models by setting `env.allowRemoteModels` to `false`:
|
||||
|
||||
```javascript
|
||||
// Disable the loading of remote models from the Hugging Face Hub:
|
||||
env.allowRemoteModels = false;
|
||||
```
|
||||
@@ -1,509 +0,0 @@
|
||||
|
||||
# Building a React application
|
||||
|
||||
In this tutorial, we'll be building a simple React application that performs multilingual translation using Transformers.js! The final product will look something like this:
|
||||
|
||||

|
||||
|
||||
Useful links:
|
||||
- [Demo site](https://huggingface.co/spaces/Xenova/react-translator)
|
||||
- [Source code](https://github.com/xenova/transformers.js/tree/main/examples/react-translator)
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org/en/) version 18+
|
||||
- [npm](https://www.npmjs.com/) version 9+
|
||||
|
||||
|
||||
## Step 1: Initialise the project
|
||||
|
||||
For this tutorial, we will use [Vite](https://vitejs.dev/) to initialise our project. Vite is a build tool that allows us to quickly set up a React application with minimal configuration. Run the following command in your terminal:
|
||||
|
||||
```bash
|
||||
npm create vite@latest react-translator -- --template react
|
||||
```
|
||||
|
||||
If prompted to install `create-vite`, type <kbd>y</kbd> and press <kbd>Enter</kbd>.
|
||||
|
||||
Next, enter the project directory and install the necessary development dependencies:
|
||||
|
||||
```bash
|
||||
cd react-translator
|
||||
npm install
|
||||
```
|
||||
|
||||
To test that our application is working, we can run the following command:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Visiting the URL shown in the terminal (e.g., [http://localhost:5173/](http://localhost:5173/)) should show the default "React + Vite" landing page.
|
||||
You can stop the development server by pressing <kbd>Ctrl</kbd> + <kbd>C</kbd> in the terminal.
|
||||
|
||||
## Step 2: Install and configure Transformers.js
|
||||
|
||||
Now we get to the fun part: adding machine learning to our application! First, install Transformers.js from [NPM](https://www.npmjs.com/package/@xenova/transformers) with the following command:
|
||||
|
||||
```bash
|
||||
npm install @xenova/transformers
|
||||
```
|
||||
|
||||
For this application, we will use the [Xenova/nllb-200-distilled-600M](https://huggingface.co/Xenova/nllb-200-distilled-600M) model, which can perform multilingual translation among 200 languages. Before we start, there are 2 things we need to take note of:
|
||||
1. ML inference can be quite computationally intensive, so it's better to load and run the models in a separate thread from the main (UI) thread.
|
||||
2. Since the model is quite large (>1 GB), we don't want to download it until the user clicks the "Translate" button.
|
||||
|
||||
We can achieve both of these goals by using a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) and some [React hooks](https://react.dev/reference/react).
|
||||
|
||||
1. Create a file called `worker.js` in the `src` directory. This script will do all the heavy-lifing for us, including loading and running of the translation pipeline. To ensure the model is only loaded once, we will create the `MyTranslationPipeline` class which use the [singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) to lazily create a single instance of the pipeline when `getInstance` is first called, and use this pipeline for all subsequent calls:
|
||||
```javascript
|
||||
import { pipeline } from '@xenova/transformers';
|
||||
|
||||
class MyTranslationPipeline {
|
||||
static task = 'translation';
|
||||
static model = 'Xenova/nllb-200-distilled-600M';
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
this.instance = pipeline(this.task, this.model, { progress_callback });
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Modify `App.jsx` in the `src` directory. This file is automatically created when initializing our React project, and will contain some boilerplate code. Inside the `App` function, let's create the web worker and store a reference to it using the `useRef` hook:
|
||||
```jsx
|
||||
// Remember to import the relevant hooks
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
function App() {
|
||||
// Create a reference to the worker object.
|
||||
const worker = useRef(null);
|
||||
|
||||
// We use the `useEffect` hook to setup the worker as soon as the `App` component is mounted.
|
||||
useEffect(() => {
|
||||
if (!worker.current) {
|
||||
// Create the worker if it does not yet exist.
|
||||
worker.current = new Worker(new URL('./worker.js', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
}
|
||||
|
||||
// Create a callback function for messages from the worker thread.
|
||||
const onMessageReceived = (e) => {
|
||||
// TODO: Will fill in later
|
||||
};
|
||||
|
||||
// Attach the callback function as an event listener.
|
||||
worker.current.addEventListener('message', onMessageReceived);
|
||||
|
||||
// Define a cleanup function for when the component is unmounted.
|
||||
return () => worker.current.removeEventListener('message', onMessageReceived);
|
||||
});
|
||||
|
||||
return (
|
||||
// TODO: Rest of our app goes here...
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Step 3: Design the user interface
|
||||
|
||||
<Tip>
|
||||
|
||||
We recommend starting the development server again with `npm run dev`
|
||||
(if not already running) so that you can see your changes in real-time.
|
||||
|
||||
</Tip>
|
||||
|
||||
|
||||
|
||||
First, let's define our components. Create a folder called `components` in the `src` directory, and create the following files:
|
||||
1. `LanguageSelector.jsx`: This component will allow the user to select the input and output languages. Check out the full list of languages [here](https://github.com/xenova/transformers.js/blob/main/examples/react-translator/src/components/LanguageSelector.jsx).
|
||||
```jsx
|
||||
const LANGUAGES = {
|
||||
"Acehnese (Arabic script)": "ace_Arab",
|
||||
"Acehnese (Latin script)": "ace_Latn",
|
||||
"Afrikaans": "afr_Latn",
|
||||
...
|
||||
"Zulu": "zul_Latn",
|
||||
}
|
||||
|
||||
export default function LanguageSelector({ type, onChange, defaultLanguage }) {
|
||||
return (
|
||||
<div className='language-selector'>
|
||||
<label>{type}: </label>
|
||||
<select onChange={onChange} defaultValue={defaultLanguage}>
|
||||
{Object.entries(LANGUAGES).map(([key, value]) => {
|
||||
return <option key={key} value={value}>{key}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
2. `Progress.jsx`: This component will display the progress for downloading each model file.
|
||||
```jsx
|
||||
export default function Progress({ text, percentage }) {
|
||||
percentage = percentage ?? 0;
|
||||
return (
|
||||
<div className="progress-container">
|
||||
<div className='progress-bar' style={{ 'width': `${percentage}%` }}>
|
||||
{text} ({`${percentage.toFixed(2)}%`})
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
We can now use these components in `App.jsx` by adding these imports to the top of the file:
|
||||
```jsx
|
||||
import LanguageSelector from './components/LanguageSelector';
|
||||
import Progress from './components/Progress';
|
||||
```
|
||||
|
||||
Let's also add some state variables to keep track of a few things in our application, like model loading, languages, input text, and output text. Add the following code to the beginning of the `App` function in `src/App.jsx`:
|
||||
```jsx
|
||||
function App() {
|
||||
|
||||
// Model loading
|
||||
const [ready, setReady] = useState(null);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [progressItems, setProgressItems] = useState([]);
|
||||
|
||||
// Inputs and outputs
|
||||
const [input, setInput] = useState('I love walking my dog.');
|
||||
const [sourceLanguage, setSourceLanguage] = useState('eng_Latn');
|
||||
const [targetLanguage, setTargetLanguage] = useState('fra_Latn');
|
||||
const [output, setOutput] = useState('');
|
||||
|
||||
// rest of the code...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Next, we can add our custom components to the main `App` component. We will also add two `textarea` elements for input and output text, and a `button` to trigger the translation. Modify the `return` statement to look like this:
|
||||
|
||||
```jsx
|
||||
return (
|
||||
<>
|
||||
<h1>Transformers.js</h1>
|
||||
<h2>ML-powered multilingual translation in React!</h2>
|
||||
|
||||
<div className='container'>
|
||||
<div className='language-container'>
|
||||
<LanguageSelector type={"Source"} defaultLanguage={"eng_Latn"} onChange={x => setSourceLanguage(x.target.value)} />
|
||||
<LanguageSelector type={"Target"} defaultLanguage={"fra_Latn"} onChange={x => setTargetLanguage(x.target.value)} />
|
||||
</div>
|
||||
|
||||
<div className='textbox-container'>
|
||||
<textarea value={input} rows={3} onChange={e => setInput(e.target.value)}></textarea>
|
||||
<textarea value={output} rows={3} readOnly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button disabled={disabled} onClick={translate}>Translate</button>
|
||||
|
||||
<div className='progress-bars-container'>
|
||||
{ready === false && (
|
||||
<label>Loading models... (only run once)</label>
|
||||
)}
|
||||
{progressItems.map(data => (
|
||||
<div key={data.file}>
|
||||
<Progress text={data.file} percentage={data.progress} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
Don't worry about the `translate` function for now. We will define it in the next section.
|
||||
|
||||
Finally, we can add some CSS to make our app look a little nicer. Modify the following files in the `src` directory:
|
||||
1. `index.css`:
|
||||
<details>
|
||||
<summary>View code</summary>
|
||||
|
||||
```css
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0.3em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea {
|
||||
padding: 0.6em;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.6em 1.2em;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
select,
|
||||
textarea,
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
font-size: 1em;
|
||||
font-family: inherit;
|
||||
background-color: #f9f9f9;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
|
||||
select:hover,
|
||||
textarea:hover,
|
||||
button:not([disabled]):hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
|
||||
select:focus,
|
||||
select:focus-visible,
|
||||
textarea:focus,
|
||||
textarea:focus-visible,
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
1. `App.css`
|
||||
<details>
|
||||
<summary>View code</summary>
|
||||
|
||||
```css
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.language-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.textbox-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
.textbox-container>textarea, .language-selector {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.language-selector>select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
color: white;
|
||||
background-color: #e9ecef;
|
||||
border: solid 1px;
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
padding: 0 4px;
|
||||
z-index: 0;
|
||||
top: 0;
|
||||
width: 1%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #007bff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.selector-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.progress-bars-container {
|
||||
padding: 8px;
|
||||
height: 140px;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 25px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
## Step 4: Connecting everything together
|
||||
|
||||
|
||||
Now that we have a basic user interface set up, we can finally connect everything together.
|
||||
|
||||
First, let's define the `translate` function, which will be called when the user clicks the `Translate` button. This sends a message (containing the input text, source language, and target language) to the worker thread for processing. We will also disable the button so the user doesn't click it multiple times. Add the following code just before the `return` statement in the `App` function:
|
||||
|
||||
```jsx
|
||||
const translate = () => {
|
||||
setDisabled(true);
|
||||
worker.current.postMessage({
|
||||
text: input,
|
||||
src_lang: sourceLanguage,
|
||||
tgt_lang: targetLanguage,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's add an event listener in `src/worker.js` to listen for messages from the main thread. We will send back messages (e.g., for model loading progress and text streaming) to the main thread with `self.postMessage`.
|
||||
|
||||
```javascript
|
||||
// Listen for messages from the main thread
|
||||
self.addEventListener('message', async (event) => {
|
||||
// Retrieve the translation pipeline. When called for the first time,
|
||||
// this will load the pipeline and save it for future use.
|
||||
let translator = await MyTranslationPipeline.getInstance(x => {
|
||||
// We also add a progress callback to the pipeline so that we can
|
||||
// track model loading.
|
||||
self.postMessage(x);
|
||||
});
|
||||
|
||||
// Actually perform the translation
|
||||
let output = await translator(event.data.text, {
|
||||
tgt_lang: event.data.tgt_lang,
|
||||
src_lang: event.data.src_lang,
|
||||
|
||||
// Allows for partial output
|
||||
callback_function: x => {
|
||||
self.postMessage({
|
||||
status: 'update',
|
||||
output: translator.tokenizer.decode(x[0].output_token_ids, { skip_special_tokens: true })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Send the output back to the main thread
|
||||
self.postMessage({
|
||||
status: 'complete',
|
||||
output: output,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Finally, let's fill in our `onMessageReceived` function, which will update the application state in response to messages from the worker thread. Add the following code inside the `useEffect` hook we defined earlier:
|
||||
|
||||
```jsx
|
||||
const onMessageReceived = (e) => {
|
||||
switch (e.data.status) {
|
||||
case 'initiate':
|
||||
// Model file start load: add a new progress item to the list.
|
||||
setReady(false);
|
||||
setProgressItems(prev => [...prev, e.data]);
|
||||
break;
|
||||
|
||||
case 'progress':
|
||||
// Model file progress: update one of the progress items.
|
||||
setProgressItems(
|
||||
prev => prev.map(item => {
|
||||
if (item.file === e.data.file) {
|
||||
return { ...item, progress: e.data.progress }
|
||||
}
|
||||
return item;
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case 'done':
|
||||
// Model file loaded: remove the progress item from the list.
|
||||
setProgressItems(
|
||||
prev => prev.filter(item => item.file !== e.data.file)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'ready':
|
||||
// Pipeline ready: the worker is ready to accept messages.
|
||||
setReady(true);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
// Generation update: update the output text.
|
||||
setOutput(e.data.output);
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
// Generation complete: re-enable the "Translate" button
|
||||
setDisabled(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
You can now run the application with `npm run dev` and perform multilingual translation directly in your browser!
|
||||
|
||||
|
||||
|
||||
## (Optional) Step 5: Build and deploy
|
||||
|
||||
To build your application, simply run `npm run build`. This will bundle your application and output the static files to the `dist` folder.
|
||||
|
||||
For this demo, we will deploy our application as a static [Hugging Face Space](https://huggingface.co/docs/hub/spaces), but you can deploy it anywhere you like! If you haven't already, you can create a free Hugging Face account [here](https://huggingface.co/join).
|
||||
|
||||
1. Visit [https://huggingface.co/new-space](https://huggingface.co/new-space) and fill in the form. Remember to select "Static" as the space type.
|
||||
2. Go to "Files" → "Add file" → "Upload files". Drag the `index.html` file and `public/` folder from the `dist` folder into the upload box and click "Upload". After they have uploaded, scroll down to the button and click "Commit changes to main".
|
||||
|
||||
**That's it!** Your application should now be live at `https://huggingface.co/spaces/<your-username>/<your-space-name>`!
|
||||
@@ -1,289 +0,0 @@
|
||||
# Building a Vanilla JavaScript Application
|
||||
|
||||
In this tutorial, you’ll build a simple web application that detects objects in images using Transformers.js! To follow along, all you need is a code editor, a browser, and a simple server (e.g., VS Code Live Server).
|
||||
|
||||
Here's how it works: the user clicks “Upload image” and selects an image using an input dialog. After analysing the image with an object detection model, the predicted bounding boxes are overlaid on top of the image, like this:
|
||||
|
||||

|
||||
|
||||
Useful links:
|
||||
|
||||
- [Demo site](https://huggingface.co/spaces/Scrimba/vanilla-js-object-detector)
|
||||
- [Interactive code walk-through (scrim)](https://scrimba.com/scrim/cKm9bDAg)
|
||||
- [Source code](https://github.com/xenova/transformers.js/tree/main/examples/vanilla-js)
|
||||
|
||||
## Step 1: HTML and CSS setup
|
||||
|
||||
Before we start building with Transformers.js, we first need to lay the groundwork with some markup and styling. Create an `index.html` file with a basic HTML skeleton, and add the following `<main>` tag to the `<body>`:
|
||||
|
||||
```html
|
||||
<main class="container">
|
||||
<label class="custom-file-upload">
|
||||
<input id="file-upload" type="file" accept="image/*" />
|
||||
<img class="upload-icon" src="https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/upload-icon.png" />
|
||||
Upload image
|
||||
</label>
|
||||
<div id="image-container"></div>
|
||||
<p id="status"></p>
|
||||
</main>
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Click here to see a breakdown of this markup.</summary>
|
||||
|
||||
We’re adding an `<input>` element with `type="file"` that accepts images. This allows the user to select an image from their local file system using a popup dialog. The default styling for this element looks quite bad, so let's add some styling. The easiest way to achieve this is to wrap the `<input>` element in a `<label>`, hide the input, and then style the label as a button.
|
||||
|
||||
We’re also adding an empty `<div>` container for displaying the image, plus an empty `<p>` tag that we'll use to give status updates to the user while we download and run the model, since both of these operations take some time.
|
||||
|
||||
</details>
|
||||
|
||||
Next, add the following CSS rules in a `style.css` file and link it to the HTML:
|
||||
|
||||
```css
|
||||
html,
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 40px auto;
|
||||
width: max(50vw, 400px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.custom-file-upload {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
gap: 10px;
|
||||
border: 2px solid black;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#file-upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
#image-container {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#image-container>img {
|
||||
width: 100%;
|
||||
}
|
||||
```
|
||||
|
||||
Here's how the UI looks at this point:
|
||||
|
||||

|
||||
|
||||
## Step 2: JavaScript setup
|
||||
|
||||
With the *boring* part out of the way, let's start writing some JavaScript code! Create a file called `index.js` and link to it in `index.html` by adding the following to the end of the `<body>`:
|
||||
|
||||
```html
|
||||
<script src="./index.js" type="module"></script>
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
The `type="module"` attribute is important, as it turns our file into a [JavaScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), meaning that we’ll be able to use imports and exports.
|
||||
|
||||
</Tip>
|
||||
|
||||
Moving into `index.js`, let's import Transformers.js by adding the following line to the top of the file:
|
||||
|
||||
```js
|
||||
import { pipeline, env } from "https://cdn.jsdelivr.net/npm/@xenova/transformers@2.6.0";
|
||||
```
|
||||
|
||||
Since we will be downloading the model from the Hugging Face Hub, we can skip the local model check by setting:
|
||||
|
||||
```js
|
||||
env.allowLocalModels = false;
|
||||
```
|
||||
|
||||
Next, let's create references to the various DOM elements we will access later:
|
||||
|
||||
```js
|
||||
const fileUpload = document.getElementById("file-upload");
|
||||
const imageContainer = document.getElementById("image-container");
|
||||
const status = document.getElementById("status");
|
||||
```
|
||||
|
||||
## Step 3: Create an object detection pipeline
|
||||
|
||||
We’re finally ready to create our object detection pipeline! As a reminder, a [pipeline](./pipelines). is a high-level interface provided by the library to perform a specific task. In our case, we will instantiate an object detection pipeline with the `pipeline()` helper function.
|
||||
|
||||
Since this can take some time (especially the first time when we have to download the ~40MB model), we first update the `status` paragraph so that the user knows that we’re about to load the model.
|
||||
|
||||
```js
|
||||
status.textContent = "Loading model...";
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
To keep this tutorial simple, we'll be loading and running the model in the main (UI) thread. This is not recommended for production applications, since the UI will freeze when we're performing these actions. This is because JavaScript is a single-threaded language. To overcome this, you can use a [web worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) to download and run the model in the background. However, we’re not going to do cover that in this tutorial...
|
||||
|
||||
</Tip>
|
||||
|
||||
|
||||
We can now call the `pipeline()` function that we imported at the top of our file, to create our object detection pipeline:
|
||||
|
||||
```js
|
||||
const detector = await pipeline("object-detection", "Xenova/detr-resnet-50");
|
||||
```
|
||||
|
||||
We’re passing two arguments into the `pipeline()` function: (1) task and (2) model.
|
||||
|
||||
1. The first tells Transformers.js what kind of task we want to perform. In our case, that is `object-detection`, but there are many other tasks that the library supports, including `text-generation`, `sentiment-analysis`, `summarization`, or `automatic-speech-recognition`. See [here](https://huggingface.co/docs/transformers.js/pipelines#tasks) for the full list.
|
||||
|
||||
2. The second argument specifies which model we would like to use to solve the given task. We will use [`Xenova/detr-resnet-50`](https://huggingface.co/Xenova/detr-resnet-50), as it is a relatively small (~40MB) but powerful model for detecting objects in an image.
|
||||
|
||||
Once the function returns, we’ll tell the user that the app is ready to be used.
|
||||
|
||||
```js
|
||||
status.textContent = "Ready";
|
||||
```
|
||||
|
||||
## Step 4: Create the image uploader
|
||||
|
||||
The next step is to support uploading/selection of images. To achieve this, we will listen for "change" events from the `fileUpload` element. In the callback function, we use a `FileReader()` to read the contents of the image if one is selected (and nothing otherwise).
|
||||
|
||||
```js
|
||||
fileUpload.addEventListener("change", function (e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
// Set up a callback when the file is loaded
|
||||
reader.onload = function (e2) {
|
||||
imageContainer.innerHTML = "";
|
||||
const image = document.createElement("img");
|
||||
image.src = e2.target.result;
|
||||
imageContainer.appendChild(image);
|
||||
// detect(image); // Uncomment this line to run the model
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
```
|
||||
|
||||
Once the image has been loaded into the browser, the `reader.onload` callback function will be invoked. In it, we append the new `<img>` element to the `imageContainer` to be displayed to the user.
|
||||
|
||||
Don’t worry about the `detect(image)` function call (which is commented out) - we will explain it later! For now, try to run the app and upload an image to the browser. You should see your image displayed under the button like this:
|
||||
|
||||

|
||||
|
||||
## Step 5: Run the model
|
||||
|
||||
We’re finally ready to start interacting with Transformers.js! Let’s uncomment the `detect(image)` function call from the snippet above. Then we’ll define the function itself:
|
||||
|
||||
```js
|
||||
async function detect(img) {
|
||||
status.textContent = "Analysing...";
|
||||
const output = await detector(img.src, {
|
||||
threshold: 0.5,
|
||||
percentage: true,
|
||||
});
|
||||
status.textContent = "";
|
||||
console.log("output", output);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
<Tip>
|
||||
|
||||
NOTE: The `detect` function needs to be asynchronous, since we’ll `await` the result of the the model.
|
||||
|
||||
</Tip>
|
||||
|
||||
Once we’ve updated the `status` to "Analysing", we’re ready to perform *inference*, which simply means to run the model with some data. This is done via the `detector()` function that was returned from `pipeline()`. The first argument we’re passing is the image data (`img.src`).
|
||||
|
||||
The second argument is an options object:
|
||||
- We set the `threshold` property to `0.5`. This means that we want the model to be at least 50% confident before claiming it has detected an object in the image. The lower the threshold, the more objects it'll detect (but may misidentify objects); the higher the threshold, the fewer objects it'll detect (but may miss objects in the scene).
|
||||
- We also specify `percentage: true`, which means that we want the bounding box for the objects to be returned as percentages (instead of pixels).
|
||||
|
||||
If you now try to run the app and upload an image, you should see the following output logged to the console:
|
||||
|
||||

|
||||
|
||||
In the example above, we uploaded an image of two elephants, so the `output` variable holds an array with two objects, each containing a `label` (the string “elephant”), a `score` (indicating the model's confidence in its prediction) and a `box` object (representing the bounding box of the detected entity).
|
||||
|
||||
## Step 6: Render the boxes
|
||||
|
||||
The final step is to display the `box` coordinates as rectangles around each of the elephants.
|
||||
|
||||
At the end of our `detect()` function, we’ll run the `renderBox` function on each object in the `output` array, using `.forEach()`.
|
||||
|
||||
```js
|
||||
output.forEach(renderBox);
|
||||
```
|
||||
|
||||
Here’s the code for the `renderBox()` function with comments to help you understand what’s going on:
|
||||
|
||||
```js
|
||||
// Render a bounding box and label on the image
|
||||
function renderBox({ box, label }) {
|
||||
const { xmax, xmin, ymax, ymin } = box;
|
||||
|
||||
// Generate a random color for the box
|
||||
const color = "#" + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, 0);
|
||||
|
||||
// Draw the box
|
||||
const boxElement = document.createElement("div");
|
||||
boxElement.className = "bounding-box";
|
||||
Object.assign(boxElement.style, {
|
||||
borderColor: color,
|
||||
left: 100 * xmin + "%",
|
||||
top: 100 * ymin + "%",
|
||||
width: 100 * (xmax - xmin) + "%",
|
||||
height: 100 * (ymax - ymin) + "%",
|
||||
});
|
||||
|
||||
// Draw the label
|
||||
const labelElement = document.createElement("span");
|
||||
labelElement.textContent = label;
|
||||
labelElement.className = "bounding-box-label";
|
||||
labelElement.style.backgroundColor = color;
|
||||
|
||||
boxElement.appendChild(labelElement);
|
||||
imageContainer.appendChild(boxElement);
|
||||
}
|
||||
```
|
||||
|
||||
The bounding box and label span also need some styling, so add the following to the `style.css` file:
|
||||
|
||||
```css
|
||||
.bounding-box {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bounding-box-label {
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
||||
```
|
||||
|
||||
**And that’s it!**
|
||||
|
||||
You've now built your own fully-functional AI application that detects objects in images, which runns completely in your browser: no external server, APIs, or build tools. Pretty cool! 🥳
|
||||
|
||||

|
||||
|
||||
The app is live at the following URL: [https://huggingface.co/spaces/Scrimba/vanilla-js-object-detector](https://huggingface.co/spaces/Scrimba/vanilla-js-object-detector)
|
||||
@@ -1,20 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Adaptive Retrieval w/ Transformers.js</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "adaptive-retrieval",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xenova/transformers": "^2.15.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.4.33",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"vite": "^5.1.7"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#root {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
padding: 1rem;
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||
import './App.css'
|
||||
|
||||
const PLACEHOLDER_TEXTS = [
|
||||
"A panda is a large black-and-white bear native to China.",
|
||||
"The typical life span of a panda is 20 years in the wild.",
|
||||
"A panda's diet consists almost entirely of bamboo.",
|
||||
"Ailuropoda melanoleuca is a bear species endemic to China.",
|
||||
"I love pandas so much!",
|
||||
"Bamboo is a fast-growing, woody grass.",
|
||||
"My favorite movie is Kung Fu Panda.",
|
||||
"I love the color blue.",
|
||||
"Once upon a time, in a land far, far away...",
|
||||
"Hello world.",
|
||||
"This is an example sentence.",
|
||||
].sort(() => Math.random() - 0.5);
|
||||
|
||||
function normalize(embedding) {
|
||||
const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
|
||||
return embedding.map((val) => val / magnitude);
|
||||
}
|
||||
|
||||
function dot(a, b) {
|
||||
return a.reduce((acc, val, i) => acc + val * b[i], 0);
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [status, setStatus] = useState('idle');
|
||||
|
||||
const [source, setSource] = useState('What is a panda?');
|
||||
const [text, setText] = useState(PLACEHOLDER_TEXTS.join('\n'));
|
||||
|
||||
const [dimensions, setDimensions] = useState(768);
|
||||
|
||||
const [embeddings, setEmbeddings] = useState([]);
|
||||
const [results, setResults] = useState([]);
|
||||
|
||||
// Create a reference to the worker object.
|
||||
const worker = useRef(null);
|
||||
|
||||
// We use the `useEffect` hook to setup the worker as soon as the `App` component is mounted.
|
||||
useEffect(() => {
|
||||
if (!worker.current) {
|
||||
// Create the worker if it does not yet exist.
|
||||
worker.current = new Worker(new URL('./worker.js', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
}
|
||||
|
||||
// Create a callback function for messages from the worker thread.
|
||||
const onMessageReceived = (e) => {
|
||||
const status = e.data.status;
|
||||
if (status === 'initiate') {
|
||||
setStatus('loading');
|
||||
} else if (status === 'ready') {
|
||||
setStatus('ready');
|
||||
} else if (status === 'complete') {
|
||||
const embeddings = e.data.embeddings;
|
||||
setDimensions(embeddings[0].length);
|
||||
setEmbeddings(embeddings);
|
||||
setStatus('idle');
|
||||
}
|
||||
};
|
||||
|
||||
// Attach the callback function as an event listener.
|
||||
worker.current.addEventListener('message', onMessageReceived);
|
||||
|
||||
// Define a cleanup function for when the component is unmounted.
|
||||
return () => worker.current.removeEventListener('message', onMessageReceived);
|
||||
}, []);
|
||||
|
||||
const run = useCallback(() => {
|
||||
setStatus('processing');
|
||||
worker.current.postMessage({
|
||||
source,
|
||||
text,
|
||||
});
|
||||
}, [source, text])
|
||||
|
||||
useEffect(() => {
|
||||
if (embeddings.length === 0) return;
|
||||
const slicedEmbeddings = embeddings.map(x => normalize(x.slice(0, dimensions)));
|
||||
|
||||
const sourceEmbedding = slicedEmbeddings[0];
|
||||
const sentenceEmbeddings = slicedEmbeddings.slice(1);
|
||||
|
||||
// Compute the cosine similarity between the source sentence and the other sentences.
|
||||
// NOTE: Since vectors are normalized, we use the dot product.
|
||||
const similarities = sentenceEmbeddings.map((embedding) => dot(sourceEmbedding, embedding));
|
||||
|
||||
setResults(text.trim().split('\n').map((sentence, i) => ({
|
||||
sentence,
|
||||
similarity: similarities[i]
|
||||
})).sort((a, b) => b.similarity - a.similarity));
|
||||
}, [text, embeddings, dimensions])
|
||||
|
||||
const busy = status !== 'idle';
|
||||
|
||||
return (
|
||||
<div className='flex flex-col h-full'>
|
||||
<h1 className='text-2xl md:text-4xl font-bold text-center mb-1'>Adaptive Retrieval w/ Matryoshka Embeddings</h1>
|
||||
<p className='text-lg md:text-xl font-medium text-center mb-2'>Powered by <a href='https://huggingface.co/nomic-ai/nomic-embed-text-v1.5'>Nomic Embed v1.5</a> and <a href='http://huggingface.co/docs/transformers.js'>🤗 Transformers.js</a></p>
|
||||
<div className='flex-grow flex flex-wrap p-4'>
|
||||
<div className='flex flex-col items-center gap-y-1 w-full md:w-1/2'>
|
||||
<label className='text-lg font-medium'>Query</label>
|
||||
<textarea
|
||||
placeholder='Enter source sentence.'
|
||||
className='border w-full p-1 resize-none overflow-hidden h-10'
|
||||
value={source}
|
||||
onChange={e => {
|
||||
setSource(e.target.value);
|
||||
setResults([]);
|
||||
setEmbeddings([]);
|
||||
}}
|
||||
></textarea>
|
||||
<label className='text-lg font-medium mt-1'>Text</label>
|
||||
<textarea
|
||||
placeholder='Enter sentences to compare with the source sentence. One sentence per line.'
|
||||
className='border w-full p-1 h-full resize-none'
|
||||
value={text}
|
||||
onChange={e => {
|
||||
setText(e.target.value);
|
||||
setResults([]);
|
||||
setEmbeddings([]);
|
||||
}}
|
||||
></textarea>
|
||||
|
||||
<button
|
||||
className='border py-1 px-2 bg-blue-400 rounded text-white text-lg font-medium disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
disabled={busy}
|
||||
onClick={run}>{
|
||||
!busy
|
||||
? (embeddings.length === 0 ? 'Compute Embeddings' : 'Recompute Embeddings')
|
||||
: status === 'loading'
|
||||
? 'Model loading...'
|
||||
: 'Processing'
|
||||
}</button>
|
||||
</div>
|
||||
<div className='flex flex-col items-center w-full md:w-1/2 gap-y-1'>
|
||||
{embeddings.length > 0 && (<>
|
||||
<label className='text-lg font-medium'>Dimensions</label>
|
||||
<input
|
||||
type="range"
|
||||
min="64"
|
||||
max="768"
|
||||
step="1"
|
||||
value={dimensions}
|
||||
onChange={e => {
|
||||
setDimensions(e.target.value);
|
||||
}}
|
||||
className="w-[98%] h-[10px]"
|
||||
/>
|
||||
<p className="font-bold text-sm">{dimensions}</p>
|
||||
<div className='w-full flex flex-col gap-y-1'>
|
||||
<label className='text-lg font-medium text-center mt-1'>Results</label>
|
||||
<div className='flex flex-col gap-y-1'>
|
||||
{results.map((result, i) => (
|
||||
<div key={i} className='flex gap-x-2 border mx-2 p-1'>
|
||||
<span className='font-bold'>{result.similarity.toFixed(3)}</span>
|
||||
<span>{result.sentence}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -1,47 +0,0 @@
|
||||
import { env, pipeline } from '@xenova/transformers';
|
||||
|
||||
// Skip local model check since we are downloading the model from the Hugging Face Hub.
|
||||
env.allowLocalModels = false;
|
||||
|
||||
class MyFeatureExtractionPipeline {
|
||||
static task = 'feature-extraction';
|
||||
static model = 'nomic-ai/nomic-embed-text-v1.5';
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
this.instance = pipeline(this.task, this.model, {
|
||||
quantized: true,
|
||||
progress_callback,
|
||||
});
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
// https://huggingface.co/nomic-ai/nomic-embed-text-v1.5#usage
|
||||
const SEARCH_PREFIX = 'search_query: ';
|
||||
const DOCUMENT_PREFIX = 'search_document: ';
|
||||
|
||||
// Listen for messages from the main thread
|
||||
self.addEventListener('message', async (event) => {
|
||||
// Retrieve the pipeline. When called for the first time,
|
||||
// this will load the pipeline and save it for future use.
|
||||
const extractor = await MyFeatureExtractionPipeline.getInstance(x => {
|
||||
// We also add a progress callback to the pipeline so that we can
|
||||
// track model loading.
|
||||
self.postMessage(x);
|
||||
});
|
||||
|
||||
const { source, text } = event.data;
|
||||
|
||||
const split = [
|
||||
SEARCH_PREFIX + source,
|
||||
...text.trim().split('\n').map(x => DOCUMENT_PREFIX + x),
|
||||
];
|
||||
const embeddings = await extractor(split, { pooling: 'mean', normalize: true });
|
||||
|
||||
// Send the output back to the main thread
|
||||
self.postMessage({ status: 'complete', embeddings: embeddings.tolist() });
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
env: { browser: true, es2020: true, 'node': true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': 'warn',
|
||||
'react/prop-types': 'off',
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Transformers.js - Code completion playground</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "code-completion",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xenova/transformers": "^2.4.4",
|
||||
"@monaco-editor/react": "^4.5.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.15",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@vitejs/plugin-react": "^4.0.3",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.3",
|
||||
"postcss": "^8.4.31",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"vite": "^4.5.3"
|
||||
},
|
||||
"overrides": {
|
||||
"protobufjs": "^7.2.4"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
.sidebar {
|
||||
background-color: #181818;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
body{
|
||||
background-color: #1F1F1F;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
color: white;
|
||||
/* background-color: #e9ecef; */
|
||||
border-radius: 8px;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
padding: 2px 4px;
|
||||
z-index: 0;
|
||||
top: 0;
|
||||
width: 1%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #007bff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
z-index: 2;
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
|
||||
import Editor from "@monaco-editor/react";
|
||||
import Progress from './components/Progress';
|
||||
|
||||
import './App.css'
|
||||
|
||||
|
||||
const MODELS = [
|
||||
'Xenova/tiny_starcoder_py',
|
||||
'Xenova/codegen-350M-mono',
|
||||
// 'Xenova/starcoderbase-1b',
|
||||
]
|
||||
function App() {
|
||||
// Editor setup
|
||||
const monaco = useRef(null);
|
||||
const [monacoReady, setMonacoReady] = useState(false);
|
||||
const [language, setLanguage] = useState('python'); // Only allow python for now
|
||||
|
||||
// Model loading
|
||||
const [ready, setReady] = useState(null);
|
||||
const [disabled, setDisabled] = useState(false);
|
||||
const [progressItems, setProgressItems] = useState([]);
|
||||
|
||||
// Inputs and outputs
|
||||
const [model, setModel] = useState(MODELS[0]);
|
||||
const [maxNewTokens, setMaxNewTokens] = useState(45);
|
||||
const [code, setCode] = useState('\ndef fib(n):\n """Calculates the nth Fibonacci number"""\n');
|
||||
|
||||
// Generation parameters
|
||||
const [temperature, setTemperature] = useState(0.5);
|
||||
const [topK, setTopK] = useState(5);
|
||||
const [doSample, setDoSample] = useState(false);
|
||||
|
||||
// Create a reference to the worker object.
|
||||
const worker = useRef(null);
|
||||
|
||||
// We use the `useEffect` hook to setup the worker as soon as the `App` component is mounted.
|
||||
useEffect(() => {
|
||||
if (!worker.current) {
|
||||
// Create the worker if it does not yet exist.
|
||||
worker.current = new Worker(new URL('./worker.js', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
}
|
||||
|
||||
// Create a callback function for messages from the worker thread.
|
||||
const onMessageReceived = (e) => {
|
||||
switch (e.data.status) {
|
||||
case 'initiate':
|
||||
// Model file start load: add a new progress item to the list.
|
||||
setReady(false);
|
||||
setProgressItems(prev => [...prev, e.data]);
|
||||
break;
|
||||
|
||||
case 'progress':
|
||||
// Model file progress: update one of the progress items.
|
||||
setProgressItems(
|
||||
prev => prev.map(item => {
|
||||
if (item.file === e.data.file) {
|
||||
return { ...item, ...e.data }
|
||||
}
|
||||
return item;
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case 'done':
|
||||
// Model file loaded: remove the progress item from the list.
|
||||
setProgressItems(
|
||||
prev => prev.filter(item => item.file !== e.data.file)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'ready':
|
||||
// Pipeline ready: the worker is ready to accept messages.
|
||||
setReady(true);
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
// Generation update: update the output text.
|
||||
setCode(e.data.output);
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
// Generation complete: re-enable the "Generate" button
|
||||
setDisabled(false);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Attach the callback function as an event listener.
|
||||
worker.current.addEventListener('message', onMessageReceived);
|
||||
|
||||
// Define a cleanup function for when the component is unmounted.
|
||||
return () => worker.current.removeEventListener('message', onMessageReceived);
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const m = monaco.current;
|
||||
if (!m) return;
|
||||
|
||||
let actionRegistration = m.addAction({
|
||||
id: "generate",
|
||||
label: "Generate",
|
||||
contextMenuGroupId: "0_custom",
|
||||
run: () => {
|
||||
const val = m.getValue();
|
||||
if (!val) return;
|
||||
|
||||
worker.current.postMessage({
|
||||
model,
|
||||
text: val,
|
||||
max_new_tokens: maxNewTokens,
|
||||
temperature,
|
||||
top_k: topK,
|
||||
do_sample: doSample
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Define a cleanup function for when the component is unmounted.
|
||||
return () => actionRegistration.dispose();
|
||||
}, [monacoReady, model, maxNewTokens, temperature, topK, doSample]);
|
||||
|
||||
const showLoading = ready === false || progressItems.length > 0
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen">
|
||||
<div className="gap-1 z-50 top-0 left-0 absolute w-full h-full transition-all px-32 flex flex-col justify-center text-center" style={{
|
||||
opacity: showLoading ? 1 : 0,
|
||||
pointerEvents: showLoading ? 'all' : 'none',
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
backdropFilter: 'blur(4px)',
|
||||
}}>
|
||||
|
||||
{ready === false && (
|
||||
<label className="text-3xl p-3">Loading model...</label>
|
||||
)}
|
||||
{progressItems.map(data => (
|
||||
<div key={data.file}>
|
||||
<Progress data={data} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div>
|
||||
<Editor
|
||||
width="calc(100vw - 360px)"
|
||||
language={language}
|
||||
value={code}
|
||||
theme="vs-dark"
|
||||
onMount={m => {
|
||||
monaco.current = m;
|
||||
setMonacoReady(true);
|
||||
}}
|
||||
options={{
|
||||
fontSize: 24
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-grow sidebar p-4 flex flex-col overflow-y-auto">
|
||||
<h2 className="text-2xl font-semibold text-center mb-1">In-browser code completion</h2>
|
||||
<div className="text-center">
|
||||
Made with <a className="text-white ital underline" href="https://github.com/xenova/transformers.js">🤗 Transformers.js</a>
|
||||
</div>
|
||||
|
||||
<label className="mt-3">Model:</label>
|
||||
<select value={model} onChange={e => setModel(e.target.value)} className="p-2.5 bg-gray-50 border border-gray-200 text-gray-900 rounded-lg">
|
||||
{MODELS.map((value, i) => {
|
||||
return <option key={i} value={value}>{value}</option>
|
||||
})}
|
||||
</select>
|
||||
|
||||
<div className="mt-3 flex justify-between">
|
||||
<label>Max new tokens:</label>
|
||||
<label>{maxNewTokens}</label>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="512"
|
||||
value={maxNewTokens}
|
||||
onChange={(event) => {
|
||||
const newValue = parseInt(event.target.value);
|
||||
setMaxNewTokens(newValue);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="mt-3 flex justify-between">
|
||||
<label>Temperature:</label>
|
||||
<label>{temperature}</label>
|
||||
</div>
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
step="0.05"
|
||||
max="1"
|
||||
value={temperature}
|
||||
onChange={(event) => {
|
||||
const newValue = Number(event.target.value);
|
||||
setTemperature(newValue);
|
||||
}}
|
||||
/>
|
||||
<div className="mt-3 flex items-center">
|
||||
<input
|
||||
id="default-checkbox"
|
||||
type="checkbox"
|
||||
value={doSample}
|
||||
onInput={(event) => {
|
||||
setDoSample(event.target.checked);
|
||||
}}
|
||||
className="w-4 h-4 text-blue-600 rounded focus:ring-blue-600 ring-offset-gray-800 focus:ring-2 bg-gray-700 border-gray-600"
|
||||
/>
|
||||
<label htmlFor="default-checkbox" className="ml-2 font-medium">Sample</label>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex justify-between" style={{ opacity: doSample ? 1 : 0.2 }}
|
||||
>
|
||||
<label>Top K:</label>
|
||||
<label>{topK}</label>
|
||||
</div>
|
||||
<input
|
||||
disabled={!doSample}
|
||||
style={{ opacity: doSample ? 1 : 0.2 }}
|
||||
type="range"
|
||||
min="0"
|
||||
max="50"
|
||||
value={topK}
|
||||
onChange={(event) => {
|
||||
const newValue = parseInt(event.target.value);
|
||||
setTopK(newValue);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex-grow"></div>
|
||||
|
||||
<div className="flex gap-2 justify-center mt-3">
|
||||
|
||||
<svg className="w-6 h-6 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 .333A9.911 9.911 0 0 0 6.866 19.65c.5.092.678-.215.678-.477 0-.237-.01-1.017-.014-1.845-2.757.6-3.338-1.169-3.338-1.169a2.627 2.627 0 0 0-1.1-1.451c-.9-.615.07-.6.07-.6a2.084 2.084 0 0 1 1.518 1.021 2.11 2.11 0 0 0 2.884.823c.044-.503.268-.973.63-1.325-2.2-.25-4.516-1.1-4.516-4.9A3.832 3.832 0 0 1 4.7 7.068a3.56 3.56 0 0 1 .095-2.623s.832-.266 2.726 1.016a9.409 9.409 0 0 1 4.962 0c1.89-1.282 2.717-1.016 2.717-1.016.366.83.402 1.768.1 2.623a3.827 3.827 0 0 1 1.02 2.659c0 3.807-2.319 4.644-4.525 4.889a2.366 2.366 0 0 1 .673 1.834c0 1.326-.012 2.394-.012 2.72 0 .263.18.572.681.475A9.911 9.911 0 0 0 10 .333Z" clipRule="evenodd" />
|
||||
</svg>
|
||||
|
||||
<a className="text-white font-normal underline underline-offset-1" href="https://github.com/xenova/transformers.js/tree/main/examples/code-completion">Source code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,20 +0,0 @@
|
||||
function formatBytes(bytes, decimals = 0) {
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)), 10);
|
||||
const rounded = (bytes / Math.pow(1000, i)).toFixed(decimals);
|
||||
return rounded + " " + sizes[i];
|
||||
}
|
||||
|
||||
export default function Progress({ data }) {
|
||||
const progress = data.progress ?? 0;
|
||||
const text = data.file;
|
||||
|
||||
const a = formatBytes(data.loaded);
|
||||
const b = formatBytes(data.total);
|
||||
return (
|
||||
<div className="progress-container">
|
||||
<div className='progress-bar' style={{ 'width': `${progress}%` }}>{text} ({`${a} / ${b}`})</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -1,73 +0,0 @@
|
||||
|
||||
import { pipeline, env } from '@xenova/transformers';
|
||||
|
||||
env.allowLocalModels = false;
|
||||
|
||||
/**
|
||||
* This class uses the Singleton pattern to ensure that only one instance of the pipeline is loaded.
|
||||
*/
|
||||
class CodeCompletionPipeline {
|
||||
static task = 'text-generation';
|
||||
static model = null;
|
||||
static instance = null;
|
||||
|
||||
static async getInstance(progress_callback = null) {
|
||||
if (this.instance === null) {
|
||||
this.instance = pipeline(this.task, this.model, { progress_callback });
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for messages from the main thread
|
||||
self.addEventListener('message', async (event) => {
|
||||
const {
|
||||
model, text, max_new_tokens,
|
||||
|
||||
// Generation parameters
|
||||
temperature,
|
||||
top_k,
|
||||
do_sample,
|
||||
} = event.data;
|
||||
|
||||
if (CodeCompletionPipeline.model !== model) {
|
||||
// Invalidate model if different
|
||||
CodeCompletionPipeline.model = model;
|
||||
|
||||
if (CodeCompletionPipeline.instance !== null) {
|
||||
(await CodeCompletionPipeline.getInstance()).dispose();
|
||||
CodeCompletionPipeline.instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the code-completion pipeline. When called for the first time,
|
||||
// this will load the pipeline and save it for future use.
|
||||
let generator = await CodeCompletionPipeline.getInstance(x => {
|
||||
// We also add a progress callback to the pipeline so that we can
|
||||
// track model loading.
|
||||
self.postMessage(x);
|
||||
});
|
||||
|
||||
// Actually perform the code-completion
|
||||
let output = await generator(text, {
|
||||
max_new_tokens,
|
||||
temperature,
|
||||
top_k,
|
||||
do_sample,
|
||||
|
||||
// Allows for partial output
|
||||
callback_function: x => {
|
||||
self.postMessage({
|
||||
status: 'update',
|
||||
output: generator.tokenizer.decode(x[0].output_token_ids, { skip_special_tokens: true })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Send the output back to the main thread
|
||||
self.postMessage({
|
||||
status: 'complete',
|
||||
output: output,
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
settings: { react: { version: '18.2' } },
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,13 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Reranking w/ Transformers.js</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "cross-encoder",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xenova/transformers": "^2.15.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"postcss": "^8.4.33",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"vite": "^5.1.7"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#root {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
padding: 1rem;
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||
import './App.css'
|
||||
|
||||
const PLACEHOLDER_TEXTS = [
|
||||
"'To Kill a Mockingbird' is a novel by Harper Lee published in 1960. It was immediately successful, winning the Pulitzer Prize, and has become a classic of modern American literature.",
|
||||
"The novel 'Moby-Dick' was written by Herman Melville and first published in 1851. It is considered a masterpiece of American literature and deals with complex themes of obsession, revenge, and the conflict between good and evil.",
|
||||
"Harper Lee, an American novelist widely known for her novel 'To Kill a Mockingbird', was born in 1926 in Monroeville, Alabama. She received the Pulitzer Prize for Fiction in 1961.",
|
||||
"Jane Austen was an English novelist known primarily for her six major novels, which interpret, critique and comment upon the British landed gentry at the end of the 18th century.",
|
||||
"The 'Harry Potter' series, which consists of seven fantasy novels written by British author J.K. Rowling, is among the most popular and critically acclaimed books of the modern era.",
|
||||
"'The Great Gatsby', a novel written by American author F. Scott Fitzgerald, was published in 1925. The story is set in the Jazz Age and follows the life of millionaire Jay Gatsby and his pursuit of Daisy Buchanan."
|
||||
].sort(() => Math.random() - 0.5);
|
||||
|
||||
function App() {
|
||||
const [status, setStatus] = useState('idle');
|
||||
|
||||
const [query, setQuery] = useState(`Who wrote 'To Kill a Mockingbird'?`);
|
||||
const [documents, setDocuments] = useState(PLACEHOLDER_TEXTS.join('\n'));
|
||||
|
||||
const [results, setResults] = useState([]);
|
||||
|
||||
// Create a reference to the worker object.
|
||||
const worker = useRef(null);
|
||||
|
||||
// We use the `useEffect` hook to setup the worker as soon as the `App` component is mounted.
|
||||
useEffect(() => {
|
||||
if (!worker.current) {
|
||||
// Create the worker if it does not yet exist.
|
||||
worker.current = new Worker(new URL('./worker.js', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
}
|
||||
|
||||
// Create a callback function for messages from the worker thread.
|
||||
const onMessageReceived = (e) => {
|
||||
const status = e.data.status;
|
||||
if (e.data.file?.endsWith('.onnx')) {
|
||||
if (status === 'initiate') {
|
||||
setStatus('loading');
|
||||
} else if (status === 'done') {
|
||||
setStatus('ready');
|
||||
}
|
||||
} else if (status === 'complete') {
|
||||
setResults(e.data.output);
|
||||
setStatus('idle');
|
||||
}
|
||||
};
|
||||
|
||||
// Attach the callback function as an event listener.
|
||||
worker.current.addEventListener('message', onMessageReceived);
|
||||
|
||||
// Define a cleanup function for when the component is unmounted.
|
||||
return () => worker.current.removeEventListener('message', onMessageReceived);
|
||||
}, []);
|
||||
|
||||
const run = useCallback(() => {
|
||||
setStatus('processing');
|
||||
worker.current.postMessage({
|
||||
query,
|
||||
documents,
|
||||
});
|
||||
}, [query, documents])
|
||||
|
||||
const busy = status !== 'idle';
|
||||
|
||||
return (
|
||||
<div className='flex flex-col h-full'>
|
||||
<h1 className='text-2xl md:text-4xl font-bold text-center mb-1'>Reranking w/ The Crispy mixedbread Rerank Models</h1>
|
||||
<p className='text-lg md:text-xl font-medium text-center mb-2'>Powered by <a href='https://huggingface.co/mixedbread-ai/mxbai-rerank-xsmall-v1' target='_blank' rel='noreferrer'>mxbai-rerank-xsmall-v1</a> and <a href='http://huggingface.co/docs/transformers.js' target='_blank' rel='noreferrer'>🤗 Transformers.js</a></p>
|
||||
<div className='flex-grow flex flex-wrap p-4'>
|
||||
<div className='flex flex-col items-center gap-y-1 w-full md:w-1/2'>
|
||||
<label className='text-lg font-medium'>Query</label>
|
||||
<textarea
|
||||
placeholder='Enter query.'
|
||||
className='border w-full p-1 resize-none overflow-hidden h-10'
|
||||
value={query}
|
||||
onChange={e => {
|
||||
setQuery(e.target.value);
|
||||
setResults([]);
|
||||
}}
|
||||
></textarea>
|
||||
<label className='text-lg font-medium mt-1'>Documents</label>
|
||||
<textarea
|
||||
placeholder='Enter documents to compare with the query. One sentence per line.'
|
||||
className='border w-full p-1 h-full resize-none'
|
||||
value={documents}
|
||||
onChange={e => {
|
||||
setDocuments(e.target.value);
|
||||
setResults([]);
|
||||
}}
|
||||
></textarea>
|
||||
|
||||
<button
|
||||
className='border py-1 px-2 bg-green-400 rounded text-white text-lg font-medium disabled:opacity-50 disabled:cursor-not-allowed'
|
||||
disabled={busy}
|
||||
onClick={run}>{
|
||||
!busy
|
||||
? 'Rerank'
|
||||
: status === 'loading'
|
||||
? 'Model loading...'
|
||||
: 'Processing'
|
||||
}</button>
|
||||
</div>
|
||||
<div className='flex flex-col items-center w-full md:w-1/2 gap-y-1'>
|
||||
{results.length > 0 && (<>
|
||||
<div className='w-full flex flex-col gap-y-1'>
|
||||
<label className='text-lg font-medium text-center'>Results</label>
|
||||
<div className='flex flex-col gap-y-1'>
|
||||
{results.map((result, i) => (
|
||||
<div key={i} className='flex gap-x-2 border mx-2 p-1'>
|
||||
<span className='font-bold'>{result.score.toFixed(3)}</span>
|
||||
<span>{result.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
@@ -1,62 +0,0 @@
|
||||
import { env, AutoTokenizer, AutoModelForSequenceClassification } from '@xenova/transformers';
|
||||
|
||||
// Skip local model check since we are downloading the model from the Hugging Face Hub.
|
||||
env.allowLocalModels = false;
|
||||
|
||||
class CrossEncoderSingleton {
|
||||
static model_id = 'mixedbread-ai/mxbai-rerank-xsmall-v1';
|
||||
static model = null;
|
||||
static tokenizer = null;
|
||||
|
||||
static async getInstance(progress_callback) {
|
||||
if (!this.tokenizer) {
|
||||
this.tokenizer = AutoTokenizer.from_pretrained(this.model_id);
|
||||
}
|
||||
|
||||
if (!this.model) {
|
||||
this.model = AutoModelForSequenceClassification.from_pretrained(this.model_id, {
|
||||
quantized: true,
|
||||
progress_callback,
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all([this.tokenizer, this.model]);
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for messages from the main thread
|
||||
self.addEventListener('message', async (event) => {
|
||||
// Retrieve the pipeline. When called for the first time,
|
||||
// this will load the pipeline and save it for future use.
|
||||
const [tokenizer, model] = await CrossEncoderSingleton.getInstance(x => {
|
||||
// We also add a progress callback to the pipeline so that we can
|
||||
// track model loading.
|
||||
self.postMessage(x);
|
||||
});
|
||||
|
||||
const { query, documents } = event.data;
|
||||
|
||||
const docs = documents.trim().split('\n');
|
||||
|
||||
const inputs = tokenizer(
|
||||
new Array(docs.length).fill(query),
|
||||
{
|
||||
text_pair: docs,
|
||||
padding: true,
|
||||
truncation: true,
|
||||
}
|
||||
)
|
||||
const { logits } = await model(inputs);
|
||||
const output = logits
|
||||
.sigmoid()
|
||||
.tolist()
|
||||
.map(([score], i) => ({
|
||||
corpus_id: i,
|
||||
score,
|
||||
text: docs[i],
|
||||
}))
|
||||
.sort((a, b) => b.score - a.score);
|
||||
|
||||
// Send the output back to the main thread
|
||||
self.postMessage({ status: 'complete', output });
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "demo-site",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^4.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xenova/transformers": "^2.0.0-alpha.3",
|
||||
"chart.js": "^4.3.0",
|
||||
"prismjs": "^1.29.0"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 413 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 246 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 221 KiB |
|
Before Width: | Height: | Size: 62 KiB |
@@ -1,600 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="author" content="Xenova" />
|
||||
<title>Transformers.js</title>
|
||||
<meta name="description"
|
||||
content="State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server!" />
|
||||
|
||||
<!-- Favicon-->
|
||||
<!-- Icon made by Freepik (https://www.flaticon.com/free-icons/robot) -->
|
||||
<link rel="icon" type="image/x-icon" href="./icons/favicon.ico" />
|
||||
|
||||
<!-- Bootstrap icons-->
|
||||
<link href="./theme.css" rel="stylesheet" />
|
||||
<link href="./css/bootstrap-icons.css" rel="stylesheet" />
|
||||
|
||||
<!-- Page JS-->
|
||||
<script type="module" src="/main.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- Responsive navbar-->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container px-5">
|
||||
<a class="navbar-brand" href="#">Transformers.js</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span
|
||||
class="navbar-toggler-icon"></span></button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item"><a class="nav-link" href="#demo">Demo</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#quick-tour">Quick tour</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="content-modal" tabindex="-1" aria-labelledby="modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modal-label">Viewing content</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Header-->
|
||||
<header class="bg-dark pt-3 pb-5">
|
||||
<div class="container px-5">
|
||||
<div class="row gx-5 justify-content-center">
|
||||
<div class="col-lg-6">
|
||||
<div class="text-center">
|
||||
<h1 class="display-5 fw-bolder text-white mb-2">Transformers.js</h1>
|
||||
<p class="lead text-white-50 mb-4">
|
||||
Run <span class="text-white">🤗</span> Transformers in your browser!
|
||||
</p>
|
||||
<div class="d-grid gap-3 d-sm-flex justify-content-sm-center">
|
||||
<a class="btn btn-primary btn-lg px-4 me-sm-3"
|
||||
href="https://huggingface.co/docs/transformers.js">Documentation</a>
|
||||
<a class="btn btn-outline-light btn-lg px-4" href="https://github.com/xenova/transformers.js">
|
||||
<i class="bi bi-github"></i> View Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Demo section-->
|
||||
<section class="py-4 border-bottom" id="demo">
|
||||
<div class="container px-5 my-4">
|
||||
<div class="mb-2">
|
||||
<h2 class="fw-bolder">Demo</h2>
|
||||
<p class="lead mb-0">Play around with some of these models:</p>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<label>Task: </label>
|
||||
<div class="col-12 mt-1">
|
||||
<select id="task" class="form-select">
|
||||
<option value="translation" selected>
|
||||
Translation w/ t5-small (78 MB)
|
||||
</option>
|
||||
<option value="text-generation">
|
||||
Text generation w/ distilgpt2 (85 MB)
|
||||
</option>
|
||||
<option value="masked-language-modelling">
|
||||
Masked language modelling w/ bert-base-cased (110 MB)
|
||||
</option>
|
||||
<option value="sequence-classification">
|
||||
Text classification w/ bert-base-multilingual-uncased-sentiment (169 MB)
|
||||
</option>
|
||||
<option value="token-classification">
|
||||
Token classification w/ Davlan/bert-base-multilingual-cased-ner-hrl (178 MB)
|
||||
</option>
|
||||
<option value="zero-shot-classification">
|
||||
Zero-shot classification w/ typeform/distilbert-base-uncased-mnli (68 MB)
|
||||
</option>
|
||||
<option value="question-answering">
|
||||
Question answering w/ distilbert-base-uncased-distilled-squad (66 MB)
|
||||
</option>
|
||||
<option value="summarization">
|
||||
Summarization w/ distilbart-cnn-6-6 (284 MB)
|
||||
</option>
|
||||
<option value="code-completion">
|
||||
Code completion w/ Salesforce/codegen-350M-mono (369 MB)
|
||||
</option>
|
||||
<option value="automatic-speech-recognition">
|
||||
Speech to text w/ whisper-tiny.en (41 MB)
|
||||
</option>
|
||||
<option value="image-to-text">
|
||||
Image to text w/ vit-gpt2-image-captioning (246 MB)
|
||||
</option>
|
||||
<option value="image-classification">
|
||||
Image classification w/ google/vit-base-patch16-224 (88 MB)
|
||||
</option>
|
||||
<option value="zero-shot-image-classification">
|
||||
Zero-shot image classification w/ openai/clip-vit-base-patch16 (151 MB)
|
||||
</option>
|
||||
<!-- TODO: add image-segmentation demo -->
|
||||
<option value="object-detection">
|
||||
Object detection w/ facebook/detr-resnet-50 (43 MB)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="languages" task="translation" class="task-settings">
|
||||
<label class="mt-2">Languages: </label>
|
||||
<div class="d-flex">
|
||||
<div style="width: calc(50% - 20px);">
|
||||
<select id="language-from" class="form-select mt-1 col-3">
|
||||
<option value="en" selected>English</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="width: 40px;" class="d-flex justify-content-center align-items-center">to</div>
|
||||
<div style="width: calc(50% - 20px);">
|
||||
<select id="language-to" class="form-select mt-1 col-3">
|
||||
<option value="fr" selected>French</option>
|
||||
<option value="de">German</option>
|
||||
<option value="ro">Romanian</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings"
|
||||
task="translation,text-generation,code-completion,masked-language-modelling,summarization,automatic-speech-recognition,image-to-text,zero-shot-classification">
|
||||
<label class="mt-2">Parameters: </label>
|
||||
<div class="row">
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6"
|
||||
task="translation,text-generation,code-completion,summarization,automatic-speech-recognition,image-to-text">
|
||||
<div class="input-group mb-2">
|
||||
<span class="input-group-text">Max length</span>
|
||||
<input type="number" param-name="max_new_tokens" datatype="number" min="1"
|
||||
class="form-control generation-option" value="200">
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6"
|
||||
task="translation,text-generation,code-completion,summarization,automatic-speech-recognition,image-to-text">
|
||||
<div class="input-group mb-2" title="Number of beams">
|
||||
<span class="input-group-text">No. beams</span>
|
||||
<input type="number" param-name="num_beams" datatype="number" min="1" max="50"
|
||||
class="form-control generation-option" value="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6" task="masked-language-modelling">
|
||||
<div class="input-group mb-2" title="Number of samples">
|
||||
<span class="input-group-text">No. samples</span>
|
||||
<input type="number" param-name="topk" datatype="number" min="1" max="50"
|
||||
class="form-control generation-option" value="5">
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6"
|
||||
task="translation,text-generation,code-completion,summarization,automatic-speech-recognition,image-to-text">
|
||||
<div class="input-group mb-2" title="Temperature (> 0)">
|
||||
<span class="input-group-text">Temp.</span>
|
||||
<input type="number" param-name="temperature" datatype="number" min="0.001" step="1"
|
||||
class="form-control generation-option" value="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6"
|
||||
task="translation,text-generation,code-completion,summarization,automatic-speech-recognition,image-to-text">
|
||||
<div class="input-group mb-2">
|
||||
<span class="input-group-text">Top K</span>
|
||||
<input type="number" param-name="top_k" datatype="number" min="0"
|
||||
class="form-control generation-option" value="0">
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6"
|
||||
task="translation,text-generation,code-completion,summarization,automatic-speech-recognition,image-to-text">
|
||||
<div class="input-group mb-2" title="Perform multinomial sampling">
|
||||
<label class="input-group-text" for="sample-select">Sample</label>
|
||||
<select param-name="do_sample" datatype="bool" class="form-select generation-option"
|
||||
id="sample-select">
|
||||
<option value="true">Yes</option>
|
||||
<option value="false" selected>No</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="task-settings col-xl-2 col-md-4 col-sm-6" task="zero-shot-classification">
|
||||
<div class="input-group mb-2" title="Allow multiple true classes">
|
||||
<label class="input-group-text" for="multi-label-select">Multi label</label>
|
||||
<select param-name="multi_label" datatype="bool" class="form-select generation-option"
|
||||
id="multi-label-select">
|
||||
<option value="true">Yes</option>
|
||||
<option value="false" selected>No</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="sequence-classification" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<textarea id="sc-input-textbox" class="mt-3 form-control"
|
||||
rows="5">The Shawshank Redemption is a true masterpiece of cinema, a movie that deserves every bit of its status as one of the greatest films ever made. From its stellar performances to its unforgettable storytelling, everything about this film is a testament to the power of great filmmaking.</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6 mt-1" style="max-height:146px">
|
||||
<canvas id="sc-canvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="token-classification" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mt-1">
|
||||
<textarea id="tc-input-textbox" class="mt-3 form-control"
|
||||
rows="5">Hugging Face is a technology company that was founded in 2016 by Clément Delangue, Julien Chaumond, and Thomas Wolf. The company is headquartered in New York City, and is focused on developing natural language processing software and tools.</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6 mt-1" style="max-height:146px">
|
||||
<div id="tc-output" class="mt-3 p-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="zero-shot-classification" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mt-1">
|
||||
<label>Text: </label>
|
||||
<textarea id="zsc-input-textbox" class="form-control"
|
||||
rows="5">I have a problem with my iphone that needs to be resolved asap!</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6 mt-1">
|
||||
<label for="zsc-classes" class="form-label mb-0">Possible class names
|
||||
(comma-separated):</label>
|
||||
<input type="text" class="form-control" id="zsc-classes"
|
||||
value="urgent, not urgent, phone, tablet, microwave">
|
||||
<div style="max-height:146px">
|
||||
<canvas id="zsc-canvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="masked-language-modelling" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<textarea id="mlm-input-textbox" class="mt-3 form-control"
|
||||
rows="5">The goal of life is [MASK].</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<textarea id="mlm-output-textbox" class="mt-3 form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="question-answering" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p class="mt-3 mb-0">Context:</p>
|
||||
<textarea id="qa-context-textbox" class="form-control"
|
||||
rows="7">The Amazon rainforest (Portuguese: Floresta Amazônica or Amazônia; Spanish: Selva Amazónica, Amazonía or usually Amazonia; French: Forêt amazonienne; Dutch: Amazoneregenwoud), also known in English as Amazonia or the Amazon Jungle, is a moist broadleaf forest that covers most of the Amazon basin of South America. This basin encompasses 7,000,000 square kilometres (2,700,000 sq mi), of which 5,500,000 square kilometres (2,100,000 sq mi) are covered by the rainforest. This region includes territory belonging to nine nations. The majority of the forest is contained within Brazil, with 60% of the rainforest, followed by Peru with 13%, Colombia with 10%, and with minor amounts in Venezuela, Ecuador, Bolivia, Guyana, Suriname and French Guiana. States or departments in four nations contain "Amazonas" in their names. The Amazon represents over half of the planet's remaining rainforests, and comprises the largest and most biodiverse tract of tropical rainforest in the world, with an estimated 390 billion individual trees divided into 16,000 species.</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<p class="mt-3 mb-0">Question:</p>
|
||||
<textarea id="qa-question-textbox" class="form-control"
|
||||
rows="5">What proportion of the planet's rainforests are found in the Amazon?</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<p class="mt-3 mb-0">Answer:</p>
|
||||
<textarea id="qa-answer-textbox" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="translation" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<textarea id="input-textbox" class="mt-3 form-control" rows="5">Hello, how are you?</textarea>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<textarea id="output-textbox" class="mt-3 form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="summarization" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-9">
|
||||
<textarea id="summarization-input-textbox" class="mt-3 form-control"
|
||||
rows="6">The tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey building, and the tallest structure in Paris. Its base is square, measuring 125 metres (410 ft) on each side. During its construction, the Eiffel Tower surpassed the Washington Monument to become the tallest man-made structure in the world, a title it held for 41 years until the Chrysler Building in New York City was finished in 1930. It was the first structure to reach a height of 300 metres. Due to the addition of a broadcasting aerial at the top of the tower in 1957, it is now taller than the Chrysler Building by 5.2 metres (17 ft). Excluding transmitters, the Eiffel Tower is the second tallest free-standing structure in France after the Millau Viaduct.</textarea>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<textarea id="summarization-output-textbox" class="mt-3 form-control" rows="6"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="text-generation" class="task-settings">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 mt-3">
|
||||
<textarea id="text-generation-textbox" class="form-control"
|
||||
rows="10">I enjoy walking with my cute dog,</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="code-completion" class="task-settings">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12 mt-3">
|
||||
<div id="code-completion-container" class="code-container">
|
||||
<textarea spellcheck="false">def fib(n):</textarea>
|
||||
<pre aria-hidden="true">
|
||||
<code class="language-python" id="highlighting-content">def fib(n):</code>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="automatic-speech-recognition" class="task-settings">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<label class="mt-3">Audio: </label>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<select id="audio-select" class="form-select">
|
||||
<option value="./audio/jfk.wav">Example 1</option>
|
||||
<option value="./audio/ted.wav">Example 2</option>
|
||||
<option value="./audio/ted_60.wav">Example 3</option>
|
||||
<option show-custom>Custom</option>
|
||||
</select>
|
||||
|
||||
<input class="mt-2 form-control" style="display: none;" type="file" id="audio-file" name="audio-file"
|
||||
accept="audio/*">
|
||||
|
||||
<audio id="audio-player" controls class="mt-2 w-100">
|
||||
<source src="./audio/jfk.wav">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<textarea id="speech2text-output-textbox" class="form-control" rows="6"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="image-to-text" class="task-settings">
|
||||
|
||||
<div class="row pt-2">
|
||||
<div class="col-lg-6">
|
||||
<label>Image: </label>
|
||||
<select id="image-select" class="form-select">
|
||||
<option value="./images/football-match.jpg">
|
||||
Example 1
|
||||
</option>
|
||||
<option value="./images/airport.jpg">
|
||||
Example 2
|
||||
</option>
|
||||
<option value="./images/savanna.jpg">
|
||||
Example 3
|
||||
</option>
|
||||
<option show-custom>Custom</option>
|
||||
</select>
|
||||
|
||||
<input class="mt-2 form-control" style="display: none;" type="file" id="image-file" name="image-file"
|
||||
accept="image/*">
|
||||
|
||||
<img id="image-viewer" class="w-100 p-4" src="./images/football-match.jpg" crossorigin="anonymous">
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<textarea id="image2text-output-textbox" class="form-control" rows="6"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="image-classification" class="task-settings">
|
||||
|
||||
<div class="row pt-2">
|
||||
<div class="col-lg-6">
|
||||
<label>Image: </label>
|
||||
<select id="ic-select" class="form-select">
|
||||
<option value="./images/tiger.jpg">
|
||||
Example 1
|
||||
</option>
|
||||
<option value="./images/teapot.jpg">
|
||||
Example 2
|
||||
</option>
|
||||
<option value="./images/palace.jpg">
|
||||
Example 3
|
||||
</option>
|
||||
<option show-custom>Custom</option>
|
||||
</select>
|
||||
|
||||
<input class="mt-2 form-control" style="display: none;" type="file" id="ic-file" name="ic-file"
|
||||
accept="image/*">
|
||||
|
||||
<img id="ic-viewer" class="w-100 p-4" src="./images/tiger.jpg" crossorigin="anonymous">
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6" style="max-height:146px">
|
||||
<canvas id="ic-canvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div task="zero-shot-image-classification" class="task-settings">
|
||||
|
||||
<div class="row pt-2">
|
||||
|
||||
|
||||
<div class="col-lg-6">
|
||||
<label>Image: </label>
|
||||
|
||||
<select id="zsic-select" class="form-select">
|
||||
<option value="./images/football-match.jpg">
|
||||
Example 1
|
||||
</option>
|
||||
<option value="./images/airport.jpg">
|
||||
Example 2
|
||||
</option>
|
||||
<option value="./images/savanna.jpg">
|
||||
Example 3
|
||||
</option>
|
||||
<option show-custom>Custom</option>
|
||||
</select>
|
||||
|
||||
<input class="mt-2 form-control" style="display: none;" type="file" id="zsic-file" name="zsic-file"
|
||||
accept="image/*">
|
||||
|
||||
<img id="zsic-viewer" class="w-100 p-4" src="./images/football-match.jpg" crossorigin="anonymous">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 mt-1">
|
||||
<label for="zsic-classes" class="form-label mb-0">Possible class names
|
||||
(comma-separated):</label>
|
||||
<input type="text" class="form-control" id="zsic-classes" value="football, airport, animals">
|
||||
<div style="max-height:146px">
|
||||
<canvas id="zsic-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div task="object-detection" class="task-settings">
|
||||
<div class="row pt-2">
|
||||
<div class="col-lg-6">
|
||||
<label>Image: </label>
|
||||
<select id="od-select" class="form-select">
|
||||
<option value="./images/cats.jpg">
|
||||
Example 1
|
||||
</option>
|
||||
<option value="./images/football-match.jpg">
|
||||
Example 2
|
||||
</option>
|
||||
<option value="./images/airport.jpg">
|
||||
Example 3
|
||||
</option>
|
||||
<option value="./images/savanna.jpg">
|
||||
Example 4
|
||||
</option>
|
||||
<option show-custom>Custom</option>
|
||||
</select>
|
||||
|
||||
<input class="mt-2 form-control" style="display: none;" type="file" id="od-file" name="od-file"
|
||||
accept="image/*">
|
||||
|
||||
<div class="position-relative">
|
||||
<img id="od-viewer" class="w-100 h-100 p-4" src="./images/cats.jpg" crossorigin="anonymous">
|
||||
<svg id="od-overlay" preserveAspectRatio="none"
|
||||
class="position-absolute w-100 h-100 left-0 start-0 p-4" style="z-index: 1;" viewBox="0 0 240 160"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6 mt-lg-5" style="max-height:250px">
|
||||
<canvas id="od-canvas" style="height:250px"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mb-1">
|
||||
Notes:
|
||||
<ul>
|
||||
<li>Clicking <em>Generate</em> for the first time will download the corresponding model from
|
||||
the
|
||||
<a href="https://huggingface.co/models">HuggingFace Hub</a>.
|
||||
All subsequent requests will use the cached model.
|
||||
</li>
|
||||
<li>For more information about the different parameters, check out HuggingFace's
|
||||
<a href="https://huggingface.co/blog/how-to-generate">guide to text generation</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-12 mt-2 d-flex justify-content-center">
|
||||
<button id="generate" type="button" class="btn btn-primary">Generate</button>
|
||||
|
||||
<!-- <button class="btn btn-primary" type="button">
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</button> -->
|
||||
</div>
|
||||
|
||||
<div id="progress" class="col-12 mt-4" style="display: none;">
|
||||
<div class="d-flex align-items-center position-relative py-2">
|
||||
<div><strong>Loading model files...</strong> (only run once)</div>
|
||||
<div class="spinner-border position-absolute" role="status" aria-hidden="true" style="right: 0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="progress-bars" class="d-flex justify-content-center flex-column gap-2 py-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Teaser section-->
|
||||
<section class="py-4 border-bottom" id="quick-tour">
|
||||
<div class="container px-5 my-4">
|
||||
<div class="mb-3">
|
||||
<h2 class="fw-bolder">Quick tour</h2>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<h5 class="mb-2">Installation</h5>
|
||||
To install via <a href="https://www.npmjs.com/package/@xenova/transformers">NPM</a>, run:
|
||||
<pre><code class="language-bash">npm i @xenova/transformers</code></pre>
|
||||
|
||||
Alternatively, you can use it in vanilla JS, without any bundler, by using a CDN
|
||||
or static hosting. For example, using
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules">ES Modules</a>,
|
||||
you can import the library with:
|
||||
|
||||
<pre><code class="language-html"><script type="module">
|
||||
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers';
|
||||
</script></code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<h5 class="mb-2">Basic example</h5>
|
||||
It's super easy to translate from existing code!
|
||||
<div class="row gx-5">
|
||||
<div class="col-lg-6 mb-4 mb-lg-0">
|
||||
<pre><code class="language-python">from transformers import pipeline
|
||||
|
||||
# Allocate a pipeline for sentiment-analysis
|
||||
pipe = pipeline('sentiment-analysis')
|
||||
|
||||
out = pipe('I love transformers!')
|
||||
# [{'label': 'POSITIVE', 'score': 0.999806941}]</code></pre>
|
||||
<p class="mb-0 text-center">Python (original)</p>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6 mb-4 mb-lg-0">
|
||||
<pre><code class="language-js">import { pipeline } from '@xenova/transformers';
|
||||
|
||||
// Allocate a pipeline for sentiment-analysis
|
||||
let pipe = await pipeline('sentiment-analysis');
|
||||
|
||||
let out = await pipe('I love transformers!');
|
||||
// [{'label': 'POSITIVE', 'score': 0.999817686}]</code></pre>
|
||||
<p class="mb-0 text-center">JavaScript (ours)</p>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
In the same way as the Python library, you can use a different model by providing its
|
||||
name as the second argument to the pipeline function. For example:
|
||||
<pre><code class="language-js">// Use a different model for sentiment-analysis
|
||||
let pipe = await pipeline('sentiment-analysis', 'nlptown/bert-base-multilingual-uncased-sentiment');</code></pre>
|
||||
|
||||
<br>
|
||||
For the full list of available tasks and architectures, check out the
|
||||
<a href="https://huggingface.co/docs/transformers.js/index#supported-tasksmodels">documentation</a>.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer-->
|
||||
<footer class="py-3 bg-dark">
|
||||
<div class="container px-5">
|
||||
<p class="m-0 text-center text-white">Copyright © Xenova 2023</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,761 +0,0 @@
|
||||
|
||||
|
||||
import Chart from 'chart.js/auto';
|
||||
import Prism from 'prismjs';
|
||||
|
||||
// Import code and styles for supported languages
|
||||
import 'prismjs/components/prism-javascript';
|
||||
import 'prismjs/components/prism-python';
|
||||
import 'prismjs/components/prism-markdown';
|
||||
import 'prismjs/components/prism-clike';
|
||||
import 'prismjs/themes/prism.css'
|
||||
|
||||
import './theme.css';
|
||||
import './style.css';
|
||||
|
||||
// Initialise worker
|
||||
const worker = new Worker(new URL('./worker.js', import.meta.url), {
|
||||
type: 'module',
|
||||
});
|
||||
|
||||
|
||||
// Define elements
|
||||
const TASK_SELECTOR = document.getElementById('task');
|
||||
|
||||
let searchParams = new URLSearchParams(location.search);
|
||||
let defaultDemo = searchParams.get('demo');
|
||||
if (defaultDemo) {
|
||||
TASK_SELECTOR.value = defaultDemo;
|
||||
}
|
||||
|
||||
// translation inputs
|
||||
const LANGUAGE_FROM = document.getElementById('language-from');
|
||||
const LANGUAGE_TO = document.getElementById('language-to');
|
||||
const INPUT_TEXTBOX = document.getElementById('input-textbox');
|
||||
const OUTPUT_TEXTBOX = document.getElementById('output-textbox');
|
||||
|
||||
// text generation inputs
|
||||
const TEXT_GENERATION_TEXTBOX = document.getElementById('text-generation-textbox');
|
||||
|
||||
const TASKS = document.getElementsByClassName('task-settings')
|
||||
const PROGRESS = document.getElementById('progress');
|
||||
const PROGRESS_BARS = document.getElementById('progress-bars');
|
||||
|
||||
const GENERATE_BUTTON = document.getElementById('generate');
|
||||
|
||||
const MLM_INPUT_TEXTBOX = document.getElementById('mlm-input-textbox');
|
||||
const MLM_OUTPUT_TEXTBOX = document.getElementById('mlm-output-textbox');
|
||||
|
||||
const SC_INPUT_TEXTBOX = document.getElementById('sc-input-textbox');
|
||||
const SC_OUTPUT_CANVAS = document.getElementById('sc-canvas');
|
||||
|
||||
const TC_INPUT_TEXTBOX = document.getElementById('tc-input-textbox');
|
||||
const TC_OUTPUT = document.getElementById('tc-output');
|
||||
|
||||
const QA_CONTEXT_TEXTBOX = document.getElementById('qa-context-textbox');
|
||||
const QA_QUESTION_TEXTBOX = document.getElementById('qa-question-textbox');
|
||||
const QA_ANSWER_TEXTBOX = document.getElementById('qa-answer-textbox');
|
||||
|
||||
const SUMMARIZATION_INPUT_TEXTBOX = document.getElementById('summarization-input-textbox');
|
||||
const SUMMARIZATION_OUTPUT_TEXTBOX = document.getElementById('summarization-output-textbox');
|
||||
|
||||
const SPEECH2TEXT_SELECT = document.getElementById('audio-select');
|
||||
const SPEECH2TEXT_INPUT = document.getElementById('audio-file');
|
||||
const SPEECH2TEXT_AUDIO = document.getElementById('audio-player');
|
||||
const SPEECH2TEXT_OUTPUT_TEXTBOX = document.getElementById('speech2text-output-textbox');
|
||||
|
||||
|
||||
const TEXT2IMAGE_SELECT = document.getElementById('image-select');
|
||||
const TEXT2IMAGE_INPUT = document.getElementById('image-file');
|
||||
const TEXT2IMAGE_IMG = document.getElementById('image-viewer');
|
||||
const TEXT2IMAGE_OUTPUT_TEXTBOX = document.getElementById('image2text-output-textbox');
|
||||
|
||||
const IMAGE_CLASSIFICATION_SELECT = document.getElementById('ic-select');
|
||||
const IMAGE_CLASSIFICATION_INPUT = document.getElementById('ic-file');
|
||||
const IMAGE_CLASSIFICATION_IMG = document.getElementById('ic-viewer');
|
||||
const IMAGE_CLASSIFICATION_OUTPUT_CANVAS = document.getElementById('ic-canvas');
|
||||
|
||||
const CODE_COMPLETION_CONTAINER = document.getElementById('code-completion-container');
|
||||
|
||||
|
||||
const ZSIC_SELECT = document.getElementById('zsic-select');
|
||||
const ZSIC_INPUT = document.getElementById('zsic-file');
|
||||
const ZSIC_CLASSES = document.getElementById('zsic-classes');
|
||||
const ZSIC_IMG = document.getElementById('zsic-viewer');
|
||||
const ZSIC_OUTPUT_CANVAS = document.getElementById('zsic-canvas');
|
||||
|
||||
|
||||
const OD_SELECT = document.getElementById('od-select');
|
||||
const OD_INPUT = document.getElementById('od-file');
|
||||
const OD_IMG = document.getElementById('od-viewer');
|
||||
const OD_OUTPUT_OVERLAY = document.getElementById('od-overlay');
|
||||
const OD_OUTPUT_CANVAS = document.getElementById('od-canvas');
|
||||
|
||||
|
||||
const ZSC_INPUT_TEXTBOX = document.getElementById('zsc-input-textbox');
|
||||
const ZSC_CLASSES = document.getElementById('zsc-classes');
|
||||
const ZSC_OUTPUT_CANVAS = document.getElementById('zsc-canvas');
|
||||
|
||||
|
||||
const DEFAULT_GREEDY_PARAMS = {
|
||||
max_new_tokens: 50,
|
||||
num_beams: 1,
|
||||
temperature: 1,
|
||||
top_k: 0,
|
||||
do_sample: false
|
||||
}
|
||||
|
||||
const TASK_DEFAULT_PARAMS = {
|
||||
'translation': DEFAULT_GREEDY_PARAMS,
|
||||
'text-generation': {
|
||||
max_new_tokens: 100,
|
||||
num_beams: 1,
|
||||
temperature: 1,
|
||||
top_k: 20,
|
||||
do_sample: true
|
||||
},
|
||||
'code-completion': DEFAULT_GREEDY_PARAMS,
|
||||
'masked-language-modelling': {
|
||||
topk: 5 // number of samples
|
||||
},
|
||||
'sequence-classification': {},
|
||||
'token-classification': {},
|
||||
'zero-shot-classification': {
|
||||
multi_label: false
|
||||
},
|
||||
'question-answering': {},
|
||||
'summarization': {
|
||||
max_new_tokens: 50,
|
||||
num_beams: 2,
|
||||
temperature: 1,
|
||||
top_k: 0,
|
||||
do_sample: false
|
||||
},
|
||||
'automatic-speech-recognition': DEFAULT_GREEDY_PARAMS,
|
||||
'image-to-text': DEFAULT_GREEDY_PARAMS,
|
||||
'image-classification': {},
|
||||
'zero-shot-image-classification': {},
|
||||
'object-detection': {},
|
||||
};
|
||||
|
||||
[
|
||||
[SPEECH2TEXT_SELECT, SPEECH2TEXT_INPUT, SPEECH2TEXT_AUDIO],
|
||||
[TEXT2IMAGE_SELECT, TEXT2IMAGE_INPUT, TEXT2IMAGE_IMG],
|
||||
[IMAGE_CLASSIFICATION_SELECT, IMAGE_CLASSIFICATION_INPUT, IMAGE_CLASSIFICATION_IMG],
|
||||
[ZSIC_SELECT, ZSIC_INPUT, ZSIC_IMG],
|
||||
[OD_SELECT, OD_INPUT, OD_IMG],
|
||||
].forEach(x => {
|
||||
let [select, input, media] = x;
|
||||
|
||||
select.addEventListener('input', (e) => {
|
||||
if (select.options[select.selectedIndex].hasAttribute('show-custom')) {
|
||||
input.style.display = 'block';
|
||||
} else {
|
||||
input.style.display = 'none';
|
||||
|
||||
media.src = select.value
|
||||
}
|
||||
})
|
||||
|
||||
input.addEventListener("change", () => {
|
||||
const file = input.files[0];
|
||||
const url = URL.createObjectURL(file);
|
||||
media.src = url;
|
||||
});
|
||||
});
|
||||
|
||||
const NER_TAGS = {
|
||||
// tag: [textColour, backgroundColour, tagColour]
|
||||
'ORG': ['#115E59', '#CCFBF1', '#14B8A6'],
|
||||
'PER': ['#9D174D', '#FCE7F3', '#EC4899'],
|
||||
'LOC': ['#86198F', '#FAE8FF', '#D946EF'],
|
||||
}
|
||||
|
||||
|
||||
// Predefined list of unique colours
|
||||
const COLOURS = [
|
||||
'255, 99, 132',
|
||||
'54, 162, 235',
|
||||
'255, 206, 86',
|
||||
'75, 192, 192',
|
||||
'153, 102, 255',
|
||||
'255, 159, 64',
|
||||
]
|
||||
|
||||
|
||||
|
||||
OD_SELECT.addEventListener('change', () => {
|
||||
// Clear overlay and chart data on change
|
||||
OD_OUTPUT_OVERLAY.innerHTML = '';
|
||||
|
||||
const chart = CHARTS[OD_OUTPUT_CANVAS.id];
|
||||
chart.data = structuredClone(DEFAULT_DATA);
|
||||
chart.update();
|
||||
});
|
||||
|
||||
OD_OUTPUT_OVERLAY.addEventListener('mousemove', (e) => {
|
||||
let rects = OD_OUTPUT_OVERLAY.querySelectorAll('rect')
|
||||
|
||||
let colours = [];
|
||||
let borderColours = [];
|
||||
|
||||
|
||||
rects.forEach((rect, i) => {
|
||||
let colour = COLOURS[i % COLOURS.length];
|
||||
|
||||
// Display if hovering over background (tagName === 'svg')
|
||||
let toDisplay = e.target.tagName !== 'rect';
|
||||
if (!toDisplay) {
|
||||
// Perform additional check
|
||||
let bb = rect.getBoundingClientRect()
|
||||
|
||||
// Check if box intersects with current mouse positition
|
||||
toDisplay = e.clientX >= bb.left && e.clientX <= bb.right && e.clientY >= bb.top && e.clientY <= bb.bottom
|
||||
}
|
||||
|
||||
if (toDisplay) {
|
||||
// Set back to original
|
||||
rect.style.fillOpacity = 0.1;
|
||||
rect.style.opacity = 1;
|
||||
colours.push(`rgba(${colour}, 0.5)`);
|
||||
borderColours.push(`rgba(${colour}, 1)`);
|
||||
} else {
|
||||
// Hovering over a rect, so set all other rects to 0 opacity
|
||||
rect.style.fillOpacity = 0;
|
||||
rect.style.opacity = 0;
|
||||
colours.push(`rgba(${colour}, 0.05)`);
|
||||
borderColours.push(`rgba(${colour}, 0.5)`);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const chart = CHARTS['od-canvas'];
|
||||
chart.data.datasets[0].backgroundColor = colours;
|
||||
chart.data.datasets[0].borderColor = borderColours;
|
||||
chart.update();
|
||||
})
|
||||
|
||||
function updateParams(task) {
|
||||
let params = TASK_DEFAULT_PARAMS[task]
|
||||
if (!params) return;
|
||||
|
||||
for (let [key, value] of Object.entries(params)) {
|
||||
let element = document.querySelector(`.generation-option[param-name="${key}"]`)
|
||||
if (!element) continue;
|
||||
element.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters
|
||||
const GENERATION_OPTIONS = document.getElementsByClassName('generation-option');
|
||||
|
||||
|
||||
const CHART_OPTIONS = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
indexAxis: 'y',
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
x: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
padding: {
|
||||
bottom: -5,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// Initialise all code blocks
|
||||
const CODE_BLOCKS = {};
|
||||
[...document.querySelectorAll('.code-container')].forEach(element => {
|
||||
|
||||
// Guide to add editable code block:
|
||||
// https://codepen.io/WebCoder49/pen/dyNyraq
|
||||
// https://css-tricks.com/creating-an-editable-textarea-that-supports-syntax-highlighted-code/
|
||||
|
||||
const CODE_HIGHLIGHT = element.querySelector('pre');
|
||||
const CODE_HIGHLIGHT_CONTENT = element.querySelector('code');
|
||||
const CODE_COMPLETION_TEXTBOX = element.querySelector('textarea');
|
||||
|
||||
let sync_scroll = () => {
|
||||
/* Scroll result to scroll coords of event - sync with textarea */
|
||||
CODE_HIGHLIGHT.scrollTop = CODE_COMPLETION_TEXTBOX.scrollTop;
|
||||
CODE_HIGHLIGHT.scrollLeft = CODE_COMPLETION_TEXTBOX.scrollLeft;
|
||||
}
|
||||
let update = (text) => {
|
||||
// Handle final newlines (see article)
|
||||
if (text[text.length - 1] == "\n") {
|
||||
text += " ";
|
||||
}
|
||||
// Update code
|
||||
CODE_HIGHLIGHT_CONTENT.innerHTML = escapeHtml(text);
|
||||
|
||||
// Syntax Highlight
|
||||
Prism.highlightElement(CODE_HIGHLIGHT_CONTENT);
|
||||
}
|
||||
|
||||
// Update code function
|
||||
let updateCode = (text) => {
|
||||
update(text);
|
||||
sync_scroll();
|
||||
};
|
||||
|
||||
CODE_BLOCKS[element.id] = {
|
||||
update: (text) => {
|
||||
CODE_COMPLETION_TEXTBOX.value = text;
|
||||
updateCode(text);
|
||||
|
||||
// When updating, set scroll to bottom
|
||||
// https://stackoverflow.com/a/9170709
|
||||
CODE_COMPLETION_TEXTBOX.scrollTop = CODE_COMPLETION_TEXTBOX.scrollHeight;
|
||||
},
|
||||
text: () => CODE_COMPLETION_TEXTBOX.value
|
||||
};
|
||||
|
||||
CODE_COMPLETION_TEXTBOX.oninput = () => updateCode(CODE_COMPLETION_TEXTBOX.value);
|
||||
|
||||
CODE_COMPLETION_TEXTBOX.onscroll = sync_scroll;
|
||||
CODE_COMPLETION_TEXTBOX.onkeydown = (event) => {
|
||||
let code = CODE_COMPLETION_TEXTBOX.value;
|
||||
if (event.key == "Tab") {
|
||||
/* Tab key pressed */
|
||||
event.preventDefault(); // stop normal
|
||||
let before_tab = code.slice(0, CODE_COMPLETION_TEXTBOX.selectionStart); // text before tab
|
||||
let after_tab = code.slice(CODE_COMPLETION_TEXTBOX.selectionEnd, CODE_COMPLETION_TEXTBOX.value.length); // text after tab
|
||||
let cursor_pos = CODE_COMPLETION_TEXTBOX.selectionStart + 1; // where cursor moves after tab - moving forward by 1 char to after tab
|
||||
CODE_COMPLETION_TEXTBOX.value = before_tab + "\t" + after_tab; // add tab char
|
||||
// move cursor
|
||||
CODE_COMPLETION_TEXTBOX.selectionStart = cursor_pos;
|
||||
CODE_COMPLETION_TEXTBOX.selectionEnd = cursor_pos;
|
||||
update(CODE_COMPLETION_TEXTBOX.value); // Update text to include indent
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
const DEFAULT_DATA = {
|
||||
labels: ['label', 'label', 'label', 'label', 'label'],
|
||||
datasets: [{
|
||||
borderWidth: 1
|
||||
}]
|
||||
}
|
||||
|
||||
const CHARTS = {
|
||||
'sc-canvas': new Chart(SC_OUTPUT_CANVAS, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['5 stars', '4 stars', '3 stars', '2 stars', '1 star'],
|
||||
datasets: [{
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: CHART_OPTIONS,
|
||||
|
||||
}),
|
||||
'ic-canvas': new Chart(IMAGE_CLASSIFICATION_OUTPUT_CANVAS, {
|
||||
type: 'bar',
|
||||
data: structuredClone(DEFAULT_DATA),
|
||||
options: CHART_OPTIONS
|
||||
}),
|
||||
|
||||
'zsic-canvas': new Chart(ZSIC_OUTPUT_CANVAS, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['football', 'airport', 'animals'],
|
||||
datasets: [{
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: CHART_OPTIONS
|
||||
}),
|
||||
|
||||
'od-canvas': new Chart(OD_OUTPUT_CANVAS, {
|
||||
type: 'bar',
|
||||
data: structuredClone(DEFAULT_DATA),
|
||||
options: CHART_OPTIONS
|
||||
}),
|
||||
|
||||
'zsc-canvas': new Chart(ZSC_OUTPUT_CANVAS, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['urgent', 'not urgent', 'phone', 'tablet', 'microwave'],
|
||||
datasets: [{
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: CHART_OPTIONS
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
[
|
||||
[ZSIC_CLASSES, ZSIC_OUTPUT_CANVAS],
|
||||
[ZSC_CLASSES, ZSC_OUTPUT_CANVAS],
|
||||
].forEach(x => {
|
||||
let [input, chart] = x;
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
// Update labels of graph
|
||||
let chartToUpdate = CHARTS[chart.id];
|
||||
|
||||
chartToUpdate.data.labels = getZSClasses(input);
|
||||
chartToUpdate.data.datasets[0].data = new Array(chartToUpdate.data.labels.length).fill(0);
|
||||
chartToUpdate.update();
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function getZSClasses(elem) {
|
||||
// Get zero-shot classes from input element
|
||||
return elem.value.split(/\s*,+\s*/g).filter(x => x);
|
||||
}
|
||||
|
||||
function updateVisibility() {
|
||||
|
||||
// Set default parameters for task
|
||||
updateParams(TASK_SELECTOR.value);
|
||||
|
||||
for (let element of TASKS) {
|
||||
if (element.getAttribute('task').split(',').includes(TASK_SELECTOR.value)) {
|
||||
element.style.display = 'block';
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
updateVisibility();
|
||||
|
||||
// Add event listeners
|
||||
TASK_SELECTOR.addEventListener('input', updateVisibility);
|
||||
|
||||
function parseValue(value, type) {
|
||||
switch (type) {
|
||||
case 'number':
|
||||
return Number(value);
|
||||
case 'bool':
|
||||
return value === 'true'
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function isVisible(e) {
|
||||
// https://stackoverflow.com/a/38873788
|
||||
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
|
||||
}
|
||||
|
||||
GENERATE_BUTTON.addEventListener('click', async (e) => {
|
||||
// Set and pass generation settings to web worker
|
||||
let data = {
|
||||
task: TASK_SELECTOR.value,
|
||||
generation: Object.fromEntries([...GENERATION_OPTIONS]
|
||||
.filter(isVisible) // Only use parameters that are visible on screen
|
||||
.map(x => {
|
||||
let value = parseValue(x.value, x.getAttribute('datatype'));
|
||||
return [x.getAttribute('param-name'), value]
|
||||
}))
|
||||
};
|
||||
switch (TASK_SELECTOR.value) {
|
||||
case 'translation':
|
||||
data.languageFrom = LANGUAGE_FROM.value
|
||||
data.languageTo = LANGUAGE_TO.value
|
||||
data.text = INPUT_TEXTBOX.value
|
||||
data.elementIdToUpdate = OUTPUT_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'text-generation':
|
||||
data.text = TEXT_GENERATION_TEXTBOX.value
|
||||
data.elementIdToUpdate = TEXT_GENERATION_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'code-completion':
|
||||
data.text = CODE_BLOCKS[CODE_COMPLETION_CONTAINER.id].text();
|
||||
data.elementIdToUpdate = CODE_COMPLETION_CONTAINER.id
|
||||
data.targetType = 'code'
|
||||
break;
|
||||
|
||||
case 'masked-language-modelling':
|
||||
data.text = MLM_INPUT_TEXTBOX.value
|
||||
data.elementIdToUpdate = MLM_OUTPUT_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'sequence-classification':
|
||||
data.text = SC_INPUT_TEXTBOX.value
|
||||
data.elementIdToUpdate = SC_OUTPUT_CANVAS.id
|
||||
data.targetType = 'chart'
|
||||
break;
|
||||
|
||||
case 'token-classification':
|
||||
data.text = TC_INPUT_TEXTBOX.value
|
||||
data.elementIdToUpdate = TC_OUTPUT.id
|
||||
data.targetType = 'tokens'
|
||||
break;
|
||||
|
||||
case 'zero-shot-classification':
|
||||
data.text = ZSC_INPUT_TEXTBOX.value
|
||||
data.classes = getZSClasses(ZSC_CLASSES);
|
||||
data.elementIdToUpdate = ZSC_OUTPUT_CANVAS.id
|
||||
data.targetType = 'chart'
|
||||
data.updateLabels = true
|
||||
break;
|
||||
|
||||
case 'question-answering':
|
||||
data.context = QA_CONTEXT_TEXTBOX.value
|
||||
data.question = QA_QUESTION_TEXTBOX.value
|
||||
data.elementIdToUpdate = QA_ANSWER_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'summarization':
|
||||
data.text = SUMMARIZATION_INPUT_TEXTBOX.value
|
||||
data.elementIdToUpdate = SUMMARIZATION_OUTPUT_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'automatic-speech-recognition':
|
||||
const sampling_rate = 16000;
|
||||
const audioCTX = new AudioContext({ sampleRate: sampling_rate })
|
||||
|
||||
const response = await (await fetch(SPEECH2TEXT_AUDIO.currentSrc)).arrayBuffer()
|
||||
const decoded = await audioCTX.decodeAudioData(response)
|
||||
|
||||
data.audio = decoded.getChannelData(0);
|
||||
data.elementIdToUpdate = SPEECH2TEXT_OUTPUT_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'image-to-text':
|
||||
data.image = getImageDataFromImage(TEXT2IMAGE_IMG)
|
||||
data.elementIdToUpdate = TEXT2IMAGE_OUTPUT_TEXTBOX.id
|
||||
break;
|
||||
|
||||
case 'image-classification':
|
||||
data.image = getImageDataFromImage(IMAGE_CLASSIFICATION_IMG)
|
||||
data.elementIdToUpdate = IMAGE_CLASSIFICATION_OUTPUT_CANVAS.id
|
||||
data.targetType = 'chart'
|
||||
data.updateLabels = true
|
||||
break;
|
||||
|
||||
|
||||
case 'zero-shot-image-classification':
|
||||
data.image = getImageDataFromImage(ZSIC_IMG)
|
||||
data.classes = getZSClasses(ZSIC_CLASSES);
|
||||
data.elementIdToUpdate = ZSIC_OUTPUT_CANVAS.id
|
||||
data.targetType = 'chart'
|
||||
data.updateLabels = true
|
||||
break;
|
||||
|
||||
case 'object-detection':
|
||||
data.image = getImageDataFromImage(OD_IMG)
|
||||
data.targetType = 'overlay'
|
||||
data.chartId = OD_OUTPUT_CANVAS.id
|
||||
data.elementIdToUpdate = OD_OUTPUT_OVERLAY.id
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
worker.postMessage(data);
|
||||
});
|
||||
|
||||
// Handle result returned by the web worker
|
||||
|
||||
worker.addEventListener('message', (event) => {
|
||||
const message = event.data;
|
||||
|
||||
switch (message.type) {
|
||||
case 'download': // for session creation
|
||||
|
||||
if (message.data.status === 'initiate') {
|
||||
PROGRESS.style.display = 'block';
|
||||
|
||||
// create progress bar
|
||||
PROGRESS_BARS.appendChild(htmlToElement(`
|
||||
<div class="progress w-100" model="${message.data.name}" file="${message.data.file}">
|
||||
<div class="progress-bar" role="progressbar"></div>
|
||||
</div>
|
||||
`));
|
||||
|
||||
} else {
|
||||
let bar = PROGRESS_BARS.querySelector(`.progress[model="${message.data.name}"][file="${message.data.file}"]> .progress-bar`)
|
||||
|
||||
switch (message.data.status) {
|
||||
case 'progress':
|
||||
// update existing bar
|
||||
bar.style.width = message.data.progress.toFixed(2) + '%';
|
||||
bar.textContent = `${message.data.file} (${formatBytes(message.data.loaded)} / ${formatBytes(message.data.total)})`;
|
||||
break;
|
||||
|
||||
case 'done':
|
||||
// Remove the progress bar
|
||||
bar.parentElement.remove();
|
||||
break;
|
||||
|
||||
case 'ready':
|
||||
// Pipeline is ready - hide container
|
||||
PROGRESS.style.display = 'none';
|
||||
PROGRESS_BARS.innerHTML = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'update': // for generation
|
||||
let target = message.target;
|
||||
let elem = document.getElementById(target);
|
||||
|
||||
switch (message.targetType) {
|
||||
case 'code':
|
||||
CODE_BLOCKS[target].update(message.data);
|
||||
break;
|
||||
default: // is textbox
|
||||
elem.value = message.data
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'complete':
|
||||
switch (message.targetType) {
|
||||
case 'chart':
|
||||
const chartToUpdate = CHARTS[message.target];
|
||||
|
||||
let chartData = chartToUpdate.data.datasets[0].data;
|
||||
|
||||
if (message.updateLabels) {
|
||||
for (let i = 0; i < message.data.length; ++i) {
|
||||
let item = message.data[i];
|
||||
chartData[i] = item.score;
|
||||
chartToUpdate.data.labels[i] = item.label;
|
||||
}
|
||||
} else {
|
||||
// set data, ensuring labels align correctly
|
||||
for (let item of message.data) {
|
||||
chartData[
|
||||
chartToUpdate.data.labels.indexOf(item.label)
|
||||
] = item.score
|
||||
}
|
||||
}
|
||||
|
||||
chartToUpdate.update(); // update the chart
|
||||
break;
|
||||
|
||||
case 'tokens':
|
||||
let target = document.getElementById(message.target);
|
||||
target.innerHTML = '';
|
||||
|
||||
let tokens = message.data;
|
||||
|
||||
for (let token of tokens) {
|
||||
let elem;
|
||||
if (token.type === 'O') {
|
||||
elem = document.createTextNode(token.text);
|
||||
} else {
|
||||
let [textColour, backgroundColour, tagColour] = NER_TAGS[token.type];
|
||||
elem = htmlToElement(`<span class="ner-container" style="background-color: ${backgroundColour}; color: ${textColour};">${token.text}<span class="ner-tag" style="background-color: ${tagColour}; color: ${backgroundColour};">${token.type}</span></span>`);
|
||||
}
|
||||
target.appendChild(elem);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case 'overlay':
|
||||
let parent = document.getElementById(message.target);
|
||||
|
||||
// Clear previous output, just in case
|
||||
parent.innerHTML = '';
|
||||
|
||||
let viewbox = parent.viewBox.baseVal;
|
||||
|
||||
let colours = [];
|
||||
let borderColours = [];
|
||||
|
||||
let items = message.data;
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
const box = items[i].box;
|
||||
|
||||
let svgns = "http://www.w3.org/2000/svg";
|
||||
let rect = document.createElementNS(svgns, 'rect');
|
||||
|
||||
rect.setAttribute('x', viewbox.width * box.xmin);
|
||||
rect.setAttribute('y', viewbox.height * box.ymin);
|
||||
rect.setAttribute('width', viewbox.width * (box.xmax - box.xmin));
|
||||
rect.setAttribute('height', viewbox.height * (box.ymax - box.ymin));
|
||||
|
||||
const colour = COLOURS[i % COLOURS.length];
|
||||
rect.style.stroke = rect.style.fill = `rgba(${colour}, 1)`;
|
||||
|
||||
colours.push(`rgba(${colour}, 0.5)`);
|
||||
borderColours.push(`rgba(${colour}, 1)`);
|
||||
parent.appendChild(rect);
|
||||
}
|
||||
|
||||
// Update chart label and data
|
||||
const chart = CHARTS[message.chartId];
|
||||
chart.data.labels = items.map(x => x.label);
|
||||
chart.data.datasets[0] = {
|
||||
data: items.map(x => x.score),
|
||||
backgroundColor: colours,
|
||||
borderColor: borderColours
|
||||
};
|
||||
chart.update()
|
||||
break;
|
||||
default: // is text
|
||||
document.getElementById(message.target).value = message.data
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Utility functions
|
||||
|
||||
function escapeHtml(unsafe) {
|
||||
return unsafe.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll("'", ''');
|
||||
}
|
||||
|
||||
|
||||
function htmlToElement(html) {
|
||||
// https://stackoverflow.com/a/35385518
|
||||
let template = document.createElement('template');
|
||||
html = html.trim(); // Never return a text node of whitespace as the result
|
||||
template.innerHTML = html;
|
||||
return template.content.firstChild;
|
||||
}
|
||||
|
||||
function formatBytes(bytes, decimals = 0) {
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)), 10);
|
||||
const rounded = (bytes / Math.pow(1000, i)).toFixed(decimals);
|
||||
return rounded + " " + sizes[i];
|
||||
}
|
||||
|
||||
function getImageDataFromImage(original) {
|
||||
|
||||
// Helper function to get image data from image element
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = original.naturalWidth;
|
||||
canvas.height = original.naturalHeight;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
// TODO play around with ctx options?
|
||||
// ctx.patternQuality = 'bilinear';
|
||||
// ctx.quality = 'bilinear';
|
||||
// ctx.antialias = 'default';
|
||||
// ctx.imageSmoothingQuality = 'high';
|
||||
|
||||
ctx.drawImage(original, 0, 0, canvas.width, canvas.height);
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
.progress-bar {
|
||||
align-items: start;
|
||||
width: 0%;
|
||||
padding: 2px 8px;
|
||||
min-height: 22px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.form-control:checked[type=checkbox] {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
|
||||
.form-control[type=checkbox]:indeterminate {
|
||||
background-color: #0d6efd;
|
||||
border-color: #0d6efd;
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.form-control[type=checkbox] {
|
||||
border-radius: 0.25em;
|
||||
}
|
||||
|
||||
.form-control:checked {
|
||||
background-color: #0d6efd;
|
||||
border-color: #0d6efd;
|
||||
}
|
||||
|
||||
.code-container {
|
||||
height: 300px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code-container>textarea,
|
||||
.code-container>pre {
|
||||
/* Both elements need the same text and space styling so they are directly on top of each other */
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.code-container>textarea,
|
||||
.code-container>pre,
|
||||
.code-container>pre * {
|
||||
/* Also add text styles to highlighing tokens */
|
||||
font-size: 15pt;
|
||||
font-family: monospace;
|
||||
line-height: 20pt;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
|
||||
.code-container>textarea,
|
||||
.code-container>pre {
|
||||
/* In the same place */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
||||
/* Move the textarea in front of the result */
|
||||
|
||||
.code-container>textarea {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.code-container>pre {
|
||||
z-index: 0;
|
||||
white-space: pre-wrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
/* Make textarea almost completely transparent */
|
||||
|
||||
.code-container>textarea {
|
||||
color: transparent;
|
||||
background: transparent;
|
||||
caret-color: black;
|
||||
}
|
||||
|
||||
/* Can be scrolled */
|
||||
.code-container>textarea,
|
||||
.code-container>pre {
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
/* Allows textarea to scroll horizontally */
|
||||
}
|
||||
|
||||
/* No resize on textarea */
|
||||
.code-container>textarea {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
|
||||
code#highlighting-content {
|
||||
border-radius: 2px;
|
||||
/* background-color: #eee; */
|
||||
color: #111;
|
||||
}
|
||||
|
||||
#od-overlay>rect {
|
||||
fill-opacity: 0.1;
|
||||
opacity: 1;
|
||||
transition: all 0.2s ease-in-out;
|
||||
stroke-width: 2px;
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
#tc-output {
|
||||
border: 1px solid #ced4da;
|
||||
min-height: 134px;
|
||||
max-height: 134px;
|
||||
border-radius: 0.25rem;
|
||||
color: #4B5563;
|
||||
line-height: 1.75;
|
||||
margin-top: 0.5rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ner-container,
|
||||
.ner-tag {
|
||||
border-radius: 0.25rem;
|
||||
font-weight: 600;
|
||||
|
||||
}
|
||||
|
||||
.ner-container {
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.ner-tag {
|
||||
font-size: 0.75rem;
|
||||
padding-left: 0.125rem;
|
||||
padding-right: 0.125rem;
|
||||
margin-left: 0.125rem;
|
||||
}
|
||||
|
||||
/* Override default code highlighting for operators */
|
||||
.token.operator {
|
||||
background: none;
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Worker.js file for doing all transformer-based computations //
|
||||
// Needed to ensure the UI thread is not blocked when running //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
import { pipeline, env } from "@xenova/transformers";
|
||||
env.allowLocalModels = false;
|
||||
|
||||
// Define task function mapping
|
||||
const TASK_FUNCTION_MAPPING = {
|
||||
'translation': translate,
|
||||
'text-generation': text_generation,
|
||||
'code-completion': code_completion,
|
||||
'masked-language-modelling': masked_lm,
|
||||
'sequence-classification': sequence_classification,
|
||||
'token-classification': token_classification,
|
||||
'zero-shot-classification': zero_shot_classification,
|
||||
'question-answering': question_answering,
|
||||
'summarization': summarize,
|
||||
'automatic-speech-recognition': speech_to_text,
|
||||
'image-to-text': image_to_text,
|
||||
'image-classification': image_classification,
|
||||
'zero-shot-image-classification': zero_shot_image_classification,
|
||||
'object-detection': object_detection,
|
||||
}
|
||||
|
||||
// Listen for messages from UI
|
||||
self.addEventListener('message', async (event) => {
|
||||
const data = event.data;
|
||||
let fn = TASK_FUNCTION_MAPPING[data.task];
|
||||
|
||||
if (!fn) return;
|
||||
|
||||
let result = await fn(data);
|
||||
self.postMessage({
|
||||
task: data.task,
|
||||
type: 'result',
|
||||
data: result
|
||||
});
|
||||
});
|
||||
|
||||
// Define model factories
|
||||
// Ensures only one model is created of each type
|
||||
class PipelineFactory {
|
||||
static task = null;
|
||||
static model = null;
|
||||
|
||||
// NOTE: instance stores a promise that resolves to the pipeline
|
||||
static instance = null;
|
||||
|
||||
constructor(tokenizer, model) {
|
||||
this.tokenizer = tokenizer;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pipeline instance
|
||||
* @param {*} progressCallback
|
||||
* @returns {Promise}
|
||||
*/
|
||||
static getInstance(progressCallback = null) {
|
||||
if (this.task === null || this.model === null) {
|
||||
throw Error("Must set task and model")
|
||||
}
|
||||
if (this.instance === null) {
|
||||
this.instance = pipeline(this.task, this.model, {
|
||||
progress_callback: progressCallback
|
||||
});
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
class TranslationPipelineFactory extends PipelineFactory {
|
||||
static task = 'translation';
|
||||
static model = 'Xenova/t5-small';
|
||||
}
|
||||
|
||||
class TextGenerationPipelineFactory extends PipelineFactory {
|
||||
static task = 'text-generation';
|
||||
static model = 'Xenova/distilgpt2';
|
||||
}
|
||||
|
||||
class CodeCompletionPipelineFactory extends PipelineFactory {
|
||||
static task = 'text-generation';
|
||||
static model = 'Xenova/codegen-350M-mono';
|
||||
}
|
||||
|
||||
class MaskedLMPipelineFactory extends PipelineFactory {
|
||||
static task = 'fill-mask';
|
||||
static model = 'Xenova/bert-base-cased';
|
||||
}
|
||||
|
||||
class SequenceClassificationPipelineFactory extends PipelineFactory {
|
||||
static task = 'text-classification';
|
||||
static model = 'Xenova/bert-base-multilingual-uncased-sentiment';
|
||||
}
|
||||
|
||||
class TokenClassificationPipelineFactory extends PipelineFactory {
|
||||
static task = 'token-classification';
|
||||
static model = 'Xenova/bert-base-multilingual-cased-ner-hrl';
|
||||
}
|
||||
|
||||
class ZeroShotClassificationPipelineFactory extends PipelineFactory {
|
||||
static task = 'zero-shot-classification';
|
||||
static model = 'Xenova/distilbert-base-uncased-mnli';
|
||||
}
|
||||
|
||||
class QuestionAnsweringPipelineFactory extends PipelineFactory {
|
||||
static task = 'question-answering';
|
||||
static model = 'Xenova/distilbert-base-cased-distilled-squad';
|
||||
}
|
||||
|
||||
class SummarizationPipelineFactory extends PipelineFactory {
|
||||
static task = 'summarization';
|
||||
static model = 'Xenova/distilbart-cnn-6-6';
|
||||
}
|
||||
|
||||
class AutomaticSpeechRecognitionPipelineFactory extends PipelineFactory {
|
||||
static task = 'automatic-speech-recognition';
|
||||
static model = 'Xenova/whisper-tiny.en';
|
||||
}
|
||||
|
||||
class ImageToTextPipelineFactory extends PipelineFactory {
|
||||
static task = 'image-to-text';
|
||||
static model = 'Xenova/vit-gpt2-image-captioning';
|
||||
}
|
||||
|
||||
class ImageClassificationPipelineFactory extends PipelineFactory {
|
||||
static task = 'image-classification';
|
||||
static model = 'Xenova/vit-base-patch16-224';
|
||||
}
|
||||
|
||||
|
||||
class ZeroShotImageClassificationPipelineFactory extends PipelineFactory {
|
||||
static task = 'zero-shot-image-classification';
|
||||
static model = 'Xenova/clip-vit-base-patch16';
|
||||
}
|
||||
|
||||
class ObjectDetectionPipelineFactory extends PipelineFactory {
|
||||
static task = 'object-detection';
|
||||
static model = 'Xenova/detr-resnet-50';
|
||||
}
|
||||
|
||||
async function translate(data) {
|
||||
|
||||
let pipeline = await TranslationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'translation',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
// Update task based on source and target languages
|
||||
// Doing it this way prevents the same model from being loaded multiple times
|
||||
pipeline.task = `translation_${data.languageFrom}_to_${data.languageTo}`;
|
||||
|
||||
return await pipeline(data.text, {
|
||||
...data.generation,
|
||||
callback_function: function (beams) {
|
||||
const decodedText = pipeline.tokenizer.decode(beams[0].output_token_ids, {
|
||||
skip_special_tokens: true,
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
data: decodedText
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function text_generation(data) {
|
||||
|
||||
let pipeline = await TextGenerationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'text-generation',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
let text = data.text.trim();
|
||||
|
||||
return await pipeline(text, {
|
||||
...data.generation,
|
||||
callback_function: function (beams) {
|
||||
const decodedText = pipeline.tokenizer.decode(beams[0].output_token_ids, {
|
||||
skip_special_tokens: true,
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
data: decodedText
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function code_completion(data) {
|
||||
|
||||
let pipeline = await CodeCompletionPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'code-completion',
|
||||
data: data,
|
||||
});
|
||||
})
|
||||
|
||||
let text = data.text;
|
||||
|
||||
return await pipeline(text, {
|
||||
...data.generation,
|
||||
callback_function: function (beams) {
|
||||
const decodedText = pipeline.tokenizer.decode(beams[0].output_token_ids, {
|
||||
skip_special_tokens: true,
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
data: decodedText
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function masked_lm(data) {
|
||||
|
||||
let pipeline = await MaskedLMPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'masked-language-modelling',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
let output = await pipeline(data.text, data.generation)
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
data: output.map(x => x.sequence).join('\n')
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
async function sequence_classification(data) {
|
||||
|
||||
let pipeline = await SequenceClassificationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'sequence-classification',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
let outputs = await pipeline(data.text, {
|
||||
topk: 5 // return all
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
data: outputs
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function token_classification(data) {
|
||||
|
||||
let pipeline = await TokenClassificationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'token-classification',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
let outputs = await pipeline(data.text, {
|
||||
ignore_labels: [] // Return all labels
|
||||
});
|
||||
|
||||
let chunks = [];
|
||||
let currentChunk = { type: '', text: [] };
|
||||
|
||||
for (let i = 0; i < outputs.length; i++) {
|
||||
let word = pipeline.tokenizer.model.tokens_to_ids.get(outputs[i].word);
|
||||
let entity = outputs[i].entity;
|
||||
|
||||
if (entity.startsWith('B-')) { // beginning of a new chunk
|
||||
if (currentChunk.text.length > 0) { // push the current chunk if it exists
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = { type: '', text: [] };
|
||||
}
|
||||
currentChunk.type = entity.slice(2); // get the type of the chunk
|
||||
currentChunk.text = [word];
|
||||
} else if (entity.startsWith('I-')) { // continuation of a chunk
|
||||
currentChunk.text.push(word);
|
||||
} else { // not part of a chunk (O tag)
|
||||
if (currentChunk.text.length > 0) { // push the current chunk if it exists
|
||||
|
||||
if (currentChunk.type === 'O') {
|
||||
currentChunk.text.push(word);
|
||||
} else {
|
||||
chunks.push(currentChunk);
|
||||
currentChunk = { type: 'O', text: [word] };
|
||||
}
|
||||
} else {
|
||||
currentChunk = { type: 'O', text: [word] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push the last chunk if it exists
|
||||
if (currentChunk.text.length > 0) {
|
||||
chunks.push(currentChunk);
|
||||
}
|
||||
|
||||
let postProcessedChunks = chunks.map(
|
||||
x => ({
|
||||
type: x.type,
|
||||
text: pipeline.tokenizer.decode(x.text)
|
||||
})
|
||||
)
|
||||
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
data: postProcessedChunks,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function zero_shot_classification(data) {
|
||||
|
||||
let pipeline = await ZeroShotClassificationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'zero-shot-classification',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
let outputs = await pipeline(data.text, data.classes, data.generation);
|
||||
let formattedOutputs = outputs.labels.map((x, i) => {
|
||||
return {
|
||||
label: x,
|
||||
score: outputs.scores[i],
|
||||
}
|
||||
});
|
||||
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
data: formattedOutputs
|
||||
});
|
||||
}
|
||||
async function question_answering(data) {
|
||||
|
||||
let pipeline = await QuestionAnsweringPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'question-answering',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
let answer = await pipeline(data.question, data.context)
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
data: answer.answer
|
||||
});
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
async function summarize(data) {
|
||||
let pipeline = await SummarizationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'summarization',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
return await pipeline(data.text, {
|
||||
...data.generation,
|
||||
callback_function: function (beams) {
|
||||
const decodedText = pipeline.tokenizer.decode(beams[0].output_token_ids, {
|
||||
skip_special_tokens: true,
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
data: decodedText.trim()
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function speech_to_text(data) {
|
||||
let pipeline = await AutomaticSpeechRecognitionPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'automatic-speech-recognition',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
return await pipeline(data.audio, {
|
||||
// Choose good defaults for the demo
|
||||
chunk_length_s: 30,
|
||||
stride_length_s: 5,
|
||||
|
||||
...data.generation,
|
||||
callback_function: function (beams) {
|
||||
const decodedText = pipeline.tokenizer.decode(beams[0].output_token_ids, {
|
||||
skip_special_tokens: true,
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
data: decodedText.trim()
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function image_to_text(data) {
|
||||
let pipeline = await ImageToTextPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'image-to-text',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
return await pipeline(data.image, {
|
||||
...data.generation,
|
||||
callback_function: function (beams) {
|
||||
const decodedText = pipeline.tokenizer.decode(beams[0].output_token_ids, {
|
||||
skip_special_tokens: true,
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'update',
|
||||
target: data.elementIdToUpdate,
|
||||
data: decodedText.trim()
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function image_classification(data) {
|
||||
let pipeline = await ImageClassificationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'image-classification',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
let outputs = await pipeline(data.image, {
|
||||
topk: 5 // return all
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
updateLabels: data.updateLabels,
|
||||
data: outputs
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function zero_shot_image_classification(data) {
|
||||
let pipeline = await ZeroShotImageClassificationPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'image-classification',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
let outputs = await pipeline(data.image, data.classes)
|
||||
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
updateLabels: data.updateLabels,
|
||||
data: outputs
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function object_detection(data) {
|
||||
|
||||
let pipeline = await ObjectDetectionPipelineFactory.getInstance(data => {
|
||||
self.postMessage({
|
||||
type: 'download',
|
||||
task: 'object-detection',
|
||||
data: data
|
||||
});
|
||||
})
|
||||
|
||||
let outputs = await pipeline(data.image, {
|
||||
threshold: 0.9,
|
||||
percentage: true
|
||||
})
|
||||
|
||||
self.postMessage({
|
||||
type: 'complete',
|
||||
target: data.elementIdToUpdate,
|
||||
targetType: data.targetType,
|
||||
chartId: data.chartId,
|
||||
data: outputs
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import path from 'path';
|
||||
|
||||
// Needed for deploying to GitHub pages
|
||||
const BASE_PATH = process.env.BASE_PATH ?? '';
|
||||
|
||||
export default {
|
||||
// config options
|
||||
base: BASE_PATH,
|
||||
root: path.join(__dirname, 'src'),
|
||||
build: {
|
||||
outDir: path.join(__dirname, 'dist')
|
||||
},
|
||||
publicDir: path.join(__dirname, 'public'),
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,29 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Transformers.js - Depth Anything</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Depth Anything w/ 🤗 Transformers.js</h1>
|
||||
<div id="container">
|
||||
<label id="upload-button" for="upload">
|
||||
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#000"
|
||||
d="M3.5 24.3a3 3 0 0 1-1.9-.8c-.5-.5-.8-1.2-.8-1.9V2.9c0-.7.3-1.3.8-1.9.6-.5 1.2-.7 2-.7h18.6c.7 0 1.3.2 1.9.7.5.6.7 1.2.7 2v18.6c0 .7-.2 1.4-.7 1.9a3 3 0 0 1-2 .8H3.6Zm0-2.7h18.7V2.9H3.5v18.7Zm2.7-2.7h13.3c.3 0 .5 0 .6-.3v-.7l-3.7-5a.6.6 0 0 0-.6-.2c-.2 0-.4 0-.5.3l-3.5 4.6-2.4-3.3a.6.6 0 0 0-.6-.3c-.2 0-.4.1-.5.3l-2.7 3.6c-.1.2-.2.4 0 .7.1.2.3.3.6.3Z">
|
||||
</path>
|
||||
</svg>
|
||||
Click to upload image
|
||||
<label id="example">(or try example)</label>
|
||||
</label>
|
||||
</div>
|
||||
<label id="status"></label>
|
||||
<input id="upload" type="file" accept="image/*" />
|
||||
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,150 +0,0 @@
|
||||
import './style.css';
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
import { pipeline, env, RawImage } from '@xenova/transformers';
|
||||
|
||||
// Since we will download the model from the Hugging Face Hub, we can skip the local model check
|
||||
env.allowLocalModels = false;
|
||||
|
||||
// Proxy the WASM backend to prevent the UI from freezing
|
||||
env.backends.onnx.wasm.proxy = true;
|
||||
|
||||
// Constants
|
||||
const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/bread_small.png';
|
||||
const DEFAULT_SCALE = 0.75;
|
||||
|
||||
// Reference the elements that we will need
|
||||
const status = document.getElementById('status');
|
||||
const fileUpload = document.getElementById('upload');
|
||||
const imageContainer = document.getElementById('container');
|
||||
const example = document.getElementById('example');
|
||||
|
||||
// Create a new depth-estimation pipeline
|
||||
status.textContent = 'Loading model...';
|
||||
const depth_estimator = await pipeline('depth-estimation', 'Xenova/depth-anything-small-hf');
|
||||
status.textContent = 'Ready';
|
||||
|
||||
example.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
predict(EXAMPLE_URL);
|
||||
});
|
||||
|
||||
fileUpload.addEventListener('change', function (e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
// Set up a callback when the file is loaded
|
||||
reader.onload = e2 => predict(e2.target.result);
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
let onSliderChange;
|
||||
|
||||
// Predict depth map for the given image
|
||||
async function predict(url) {
|
||||
imageContainer.innerHTML = '';
|
||||
const image = await RawImage.fromURL(url);
|
||||
|
||||
// Set up scene and slider controls
|
||||
const { canvas, setDisplacementMap } = setupScene(url, image.width, image.height);
|
||||
|
||||
imageContainer.append(canvas);
|
||||
|
||||
status.textContent = 'Analysing...';
|
||||
const { depth } = await depth_estimator(image);
|
||||
|
||||
setDisplacementMap(depth.toCanvas());
|
||||
status.textContent = '';
|
||||
|
||||
// Add slider control
|
||||
const slider = document.createElement('input');
|
||||
slider.type = 'range';
|
||||
slider.min = 0;
|
||||
slider.max = 1;
|
||||
slider.step = 0.01;
|
||||
slider.addEventListener('input', (e) => {
|
||||
onSliderChange(parseFloat(e.target.value));
|
||||
});
|
||||
slider.defaultValue = DEFAULT_SCALE;
|
||||
imageContainer.append(slider);
|
||||
}
|
||||
|
||||
function setupScene(url, w, h) {
|
||||
|
||||
// Create new scene
|
||||
const canvas = document.createElement('canvas');
|
||||
const width = canvas.width = imageContainer.offsetWidth;
|
||||
const height = canvas.height = imageContainer.offsetHeight;
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
|
||||
// Create camera and add it to the scene
|
||||
const camera = new THREE.PerspectiveCamera(30, width / height, 0.01, 10);
|
||||
camera.position.z = 2;
|
||||
scene.add(camera);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
|
||||
renderer.setSize(width, height);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
|
||||
// Add ambient light
|
||||
const light = new THREE.AmbientLight(0xffffff, 2);
|
||||
scene.add(light);
|
||||
|
||||
// Load depth texture
|
||||
const image = new THREE.TextureLoader().load(url);
|
||||
image.colorSpace = THREE.SRGBColorSpace;
|
||||
const material = new THREE.MeshStandardMaterial({
|
||||
map: image,
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
material.displacementScale = DEFAULT_SCALE;
|
||||
|
||||
const setDisplacementMap = (canvas) => {
|
||||
material.displacementMap = new THREE.CanvasTexture(canvas);
|
||||
material.needsUpdate = true;
|
||||
}
|
||||
|
||||
const setDisplacementScale = (scale) => {
|
||||
material.displacementScale = scale;
|
||||
material.needsUpdate = true;
|
||||
}
|
||||
onSliderChange = setDisplacementScale;
|
||||
|
||||
// Create plane and rescale it so that max(w, h) = 1
|
||||
const [pw, ph] = w > h ? [1, h / w] : [w / h, 1];
|
||||
const geometry = new THREE.PlaneGeometry(pw, ph, w, h);
|
||||
const plane = new THREE.Mesh(geometry, material);
|
||||
scene.add(plane);
|
||||
|
||||
// Add orbit controls
|
||||
const controls = new OrbitControls(camera, renderer.domElement);
|
||||
controls.enableDamping = true;
|
||||
|
||||
renderer.setAnimationLoop(() => {
|
||||
renderer.render(scene, camera);
|
||||
controls.update();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
const width = imageContainer.offsetWidth;
|
||||
const height = imageContainer.offsetHeight;
|
||||
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
renderer.setSize(width, height);
|
||||
}, false);
|
||||
|
||||
return {
|
||||
canvas: renderer.domElement,
|
||||
setDisplacementMap,
|
||||
};
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "depth-anything-client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xenova/transformers": "^2.14.1",
|
||||
"three": "^0.160.1"
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 16px 32px;
|
||||
}
|
||||
|
||||
body,
|
||||
#container,
|
||||
#upload-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: relative;
|
||||
width: 640px;
|
||||
height: 420px;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border: 2px dashed #D1D5DB;
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
margin-top: 1rem;
|
||||
background-size: 100% 100%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#mask-output {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#upload-button {
|
||||
gap: 0.4rem;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#example {
|
||||
font-size: 14px;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#example:hover {
|
||||
color: #2563EB;
|
||||
}
|
||||
|
||||
canvas {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#status {
|
||||
min-height: 16px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { defineConfig } from 'vite';
|
||||
export default defineConfig({
|
||||
build: {
|
||||
target: 'esnext'
|
||||
}
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
.DS_Store
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# Webpack
|
||||
.webpack/
|
||||
|
||||
# Vite
|
||||
.vite/
|
||||
|
||||
# Electron-Forge
|
||||
out/
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
# Transformers.js - Sample Electron application
|
||||
|
||||
An example project to show how to run 🤗 Transformers in an [Electron](https://www.electronjs.org/) application.
|
||||
|
||||
## Getting Started
|
||||
1. Clone the repo and enter the project directory:
|
||||
```bash
|
||||
git clone https://github.com/xenova/transformers.js.git
|
||||
cd transformers.js/examples/electron/
|
||||
```
|
||||
1. Install the necessary dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
1. Run the application:
|
||||
```bash
|
||||
npm run start
|
||||
```
|
||||
|
||||
After a few seconds, a new window should pop up on your screen!
|
||||
|
||||
|
||||
## Editing the template
|
||||
|
||||
|
||||
All source code can be found in `./src/`:
|
||||
- `index.js` - Serves as the entry point for the application's main process. When an Electron app is launched, this is the first file that gets executed, and it is responsible for setting up the main process of the application. You will need to restart the application after editing this file for your changes to take effect.
|
||||
- `preload.js` - Used to preload scripts and modules in a renderer process before any other scripts run. In our case, we use the `contextBridge` API to expose the `run` function to the renderer, which runs the model in the background. You will need to restart the application after editing this file for your changes to take effect.
|
||||
- `model.js` - Contains all the logic for loading the model and running predictions. You will need to restart the application after editing this file for your changes to take effect.
|
||||
|
||||
- `client.js` - Handles interaction with the interface, as well as communication between the renderer thread (UI) and the worker thread (processing). To see changes made to this file made while editing, simply refresh the window (<kbd>Ctrl + R</kbd> or "View" → "Reload").
|
||||
- `index.html`, `index.css` - The user interface which is displayed to the user. To see changes made to this file made while editing, simply refresh the window (<kbd>Ctrl + R</kbd> or "View" → "Reload").
|
||||
@@ -1,22 +0,0 @@
|
||||
module.exports = {
|
||||
packagerConfig: {},
|
||||
rebuildConfig: {},
|
||||
makers: [
|
||||
{
|
||||
name: '@electron-forge/maker-squirrel',
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-zip',
|
||||
platforms: ['darwin'],
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-deb',
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
name: '@electron-forge/maker-rpm',
|
||||
config: {},
|
||||
},
|
||||
],
|
||||
};
|
||||