Committing too many files but the app runs possibly with a new model.
This commit is contained in:
@@ -21,6 +21,14 @@ public class EmbeddingConfiguration
|
||||
public int Dimension { get; set; } = 384;
|
||||
public string ApiToken { get; set; } = string.Empty;
|
||||
public bool UseLocalInference { get; set; } = true;
|
||||
|
||||
// Predefined model configurations
|
||||
public static class Models
|
||||
{
|
||||
public const string DefaultMiniLM = "sentence-transformers/all-MiniLM-L6-v2";
|
||||
public const string AddressTunedMiniLM = "jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3";
|
||||
public const string AddressTunedMiniLMAlias = "custom-all-MiniLM-L6-v2-address";
|
||||
}
|
||||
}
|
||||
|
||||
public class AppSettings
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# Custom Model Conversion Guide
|
||||
|
||||
This document describes how to use a custom embedding model for address embeddings.
|
||||
|
||||
## Model Source
|
||||
|
||||
The custom model is available at:
|
||||
- **HuggingFace**: [jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3](https://huggingface.co/jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3)
|
||||
|
||||
This model is a fine-tuned version of all-MiniLM-L6-v2 specifically trained on address data.
|
||||
|
||||
## Converting to ONNX Format
|
||||
|
||||
Since the model doesn't come with a pre-converted ONNX format, you need to convert it using Python.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Install the required packages:
|
||||
|
||||
```bash
|
||||
pip install optimum[exporters] transformers torch
|
||||
```
|
||||
|
||||
### Conversion Steps
|
||||
|
||||
1. **Run the conversion script**:
|
||||
|
||||
```bash
|
||||
cd VectorSearchApp/Models
|
||||
python download-convert-model.py
|
||||
```
|
||||
|
||||
This will:
|
||||
- Download the model from HuggingFace
|
||||
- Convert it to ONNX format using Optimum
|
||||
- Save the model to `Models/custom-model/`
|
||||
- Copy the main model file to `Models/address-embedding-model.onnx`
|
||||
|
||||
2. **Update configuration**:
|
||||
|
||||
Edit `VectorSearchApp/appsettings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"Embedding": {
|
||||
"ModelName": "jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3",
|
||||
"Dimension": 384,
|
||||
"ApiToken": "",
|
||||
"UseLocalInference": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or use the shorter alias:
|
||||
|
||||
```json
|
||||
{
|
||||
"Embedding": {
|
||||
"ModelName": "custom-all-MiniLM-L6-v2-address",
|
||||
"Dimension": 384,
|
||||
"ApiToken": "",
|
||||
"UseLocalInference": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. **Run the application**:
|
||||
|
||||
```bash
|
||||
cd VectorSearchApp
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Output Files
|
||||
|
||||
After conversion, the following files will be created:
|
||||
|
||||
```
|
||||
VectorSearchApp/Models/
|
||||
├── custom-model/
|
||||
│ ├── config.json
|
||||
│ ├── model.onnx
|
||||
│ ├── special_tokens_map.json
|
||||
│ ├── tokenizer.json
|
||||
│ ├── tokenizer_config.json
|
||||
│ └── vocab.txt
|
||||
└── address-embedding-model.onnx (copy of model.onnx for easy access)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### CUDA/GPU Support
|
||||
|
||||
If you want to use GPU acceleration during conversion:
|
||||
|
||||
```python
|
||||
from optimum.onnxruntime import ORTModelForFeatureExtraction
|
||||
|
||||
model = ORTModelForFeatureExtraction.from_pretrained(
|
||||
model_id,
|
||||
export=True,
|
||||
provider="CUDAExecutionProvider", # Use CUDA instead of CPU
|
||||
)
|
||||
```
|
||||
|
||||
### Large Model Download
|
||||
|
||||
The first conversion may take several minutes as it downloads the full model (~90MB) and tokenizer files.
|
||||
|
||||
### Memory Requirements
|
||||
|
||||
Conversion requires approximately 4GB of RAM. If you encounter memory issues, try closing other applications.
|
||||
Binary file not shown.
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"architectures": [
|
||||
"BertModel"
|
||||
],
|
||||
"attention_probs_dropout_prob": 0.1,
|
||||
"classifier_dropout": null,
|
||||
"dtype": "float32",
|
||||
"gradient_checkpointing": false,
|
||||
"hidden_act": "gelu",
|
||||
"hidden_dropout_prob": 0.1,
|
||||
"hidden_size": 384,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 1536,
|
||||
"layer_norm_eps": 1e-12,
|
||||
"max_position_embeddings": 512,
|
||||
"model_type": "bert",
|
||||
"num_attention_heads": 12,
|
||||
"num_hidden_layers": 6,
|
||||
"pad_token_id": 0,
|
||||
"position_embedding_type": "absolute",
|
||||
"transformers_version": "4.57.6",
|
||||
"type_vocab_size": 2,
|
||||
"use_cache": true,
|
||||
"vocab_size": 30522
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"cls_token": {
|
||||
"content": "[CLS]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false
|
||||
},
|
||||
"mask_token": {
|
||||
"content": "[MASK]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false
|
||||
},
|
||||
"pad_token": {
|
||||
"content": "[PAD]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false
|
||||
},
|
||||
"sep_token": {
|
||||
"content": "[SEP]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false
|
||||
},
|
||||
"unk_token": {
|
||||
"content": "[UNK]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"added_tokens_decoder": {
|
||||
"0": {
|
||||
"content": "[PAD]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false,
|
||||
"special": true
|
||||
},
|
||||
"100": {
|
||||
"content": "[UNK]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false,
|
||||
"special": true
|
||||
},
|
||||
"101": {
|
||||
"content": "[CLS]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false,
|
||||
"special": true
|
||||
},
|
||||
"102": {
|
||||
"content": "[SEP]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false,
|
||||
"special": true
|
||||
},
|
||||
"103": {
|
||||
"content": "[MASK]",
|
||||
"lstrip": false,
|
||||
"normalized": false,
|
||||
"rstrip": false,
|
||||
"single_word": false,
|
||||
"special": true
|
||||
}
|
||||
},
|
||||
"clean_up_tokenization_spaces": false,
|
||||
"cls_token": "[CLS]",
|
||||
"do_basic_tokenize": true,
|
||||
"do_lower_case": true,
|
||||
"extra_special_tokens": {},
|
||||
"mask_token": "[MASK]",
|
||||
"max_length": 128,
|
||||
"model_max_length": 256,
|
||||
"never_split": null,
|
||||
"pad_to_multiple_of": null,
|
||||
"pad_token": "[PAD]",
|
||||
"pad_token_type_id": 0,
|
||||
"padding_side": "right",
|
||||
"sep_token": "[SEP]",
|
||||
"stride": 0,
|
||||
"strip_accents": null,
|
||||
"tokenize_chinese_chars": true,
|
||||
"tokenizer_class": "BertTokenizer",
|
||||
"truncation_side": "right",
|
||||
"truncation_strategy": "longest_first",
|
||||
"unk_token": "[UNK]"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env pwsh
|
||||
# Download script for custom address embedding model (ONNX format)
|
||||
# This script downloads a pre-converted ONNX model if available
|
||||
|
||||
$ModelRepo = "jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3"
|
||||
$OutputPath = "Models/address-embedding-model.onnx"
|
||||
|
||||
Write-Host "Attempting to download pre-converted ONNX model..." -ForegroundColor Cyan
|
||||
|
||||
# Try to download from HuggingFace Hub (ONNX format if available)
|
||||
$OnnxUrl = "https://huggingface.co/$ModelRepo/resolve/main/onnx/model.onnx"
|
||||
|
||||
try {
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
if (Get-Command curl -ErrorAction SilentlyContinue) {
|
||||
curl -L -o $OutputPath $OnnxUrl --fail
|
||||
} else {
|
||||
Invoke-WebRequest -Uri $OnnxUrl -OutFile $OutputPath -UseBasicParsing
|
||||
}
|
||||
|
||||
if (Test-Path $OutputPath) {
|
||||
$size = (Get-Item $OutputPath).Length / 1MB
|
||||
Write-Host "Successfully downloaded model to $OutputPath" -ForegroundColor Green
|
||||
Write-Host "File size: $([math]::Round($size, 2)) MB" -ForegroundColor Gray
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "Could not download pre-converted ONNX model." -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "The model does not have a pre-converted ONNX format." -ForegroundColor White
|
||||
Write-Host "Please run the Python conversion script instead:" -ForegroundColor White
|
||||
Write-Host " python download-convert-model.py" -ForegroundColor Gray
|
||||
exit 1
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Download and convert the custom all-MiniLM-L6-v2 model to ONNX format.
|
||||
|
||||
This script:
|
||||
1. Downloads the model from HuggingFace (jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3)
|
||||
2. Converts it to ONNX format using optimum library
|
||||
3. Saves the ONNX model to the Models directory
|
||||
|
||||
Requirements:
|
||||
pip install optimum[exporters] transformers torch
|
||||
|
||||
Usage:
|
||||
python download-convert-model.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def check_requirements():
|
||||
"""Check if required packages are installed."""
|
||||
required_packages = [
|
||||
("optimum", "optimum[exporters]"),
|
||||
("transformers", "transformers"),
|
||||
("torch", "torch"),
|
||||
]
|
||||
|
||||
missing = []
|
||||
for package_name, install_command in required_packages:
|
||||
try:
|
||||
__import__(package_name.replace("-", "_"))
|
||||
print(f"[OK] {package_name} is installed")
|
||||
except ImportError:
|
||||
missing.append((package_name, install_command))
|
||||
|
||||
if missing:
|
||||
print("\nMissing required packages. Installing...")
|
||||
for package_name, install_command in missing:
|
||||
print(f"Installing {package_name}...")
|
||||
subprocess.check_call([sys.executable, "-m", "pip", "install", install_command])
|
||||
|
||||
return True
|
||||
|
||||
def download_and_convert_model():
|
||||
"""Download model from HuggingFace and convert to ONNX."""
|
||||
from optimum.onnxruntime import ORTModelForFeatureExtraction
|
||||
from transformers import AutoTokenizer
|
||||
|
||||
model_id = "jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3"
|
||||
output_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Models", "custom-model")
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Downloading model: {model_id}")
|
||||
print(f"Output directory: {output_dir}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
# Create output directory
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
try:
|
||||
print("Downloading model and tokenizer from HuggingFace...")
|
||||
print("This may take a few minutes on first run...\n")
|
||||
|
||||
# Download model and tokenizer, then export to ONNX
|
||||
model = ORTModelForFeatureExtraction.from_pretrained(
|
||||
model_id,
|
||||
export=True,
|
||||
provider="CPUExecutionProvider",
|
||||
)
|
||||
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_id)
|
||||
|
||||
# Save ONNX model
|
||||
print(f"Saving ONNX model to {output_dir}...")
|
||||
model.save_pretrained(output_dir)
|
||||
tokenizer.save_pretrained(output_dir)
|
||||
|
||||
print("\n[OK] Model successfully converted to ONNX format!")
|
||||
print(f"\nOutput files:")
|
||||
for f in os.listdir(output_dir):
|
||||
filepath = os.path.join(output_dir, f)
|
||||
size_mb = os.path.getsize(filepath) / (1024 * 1024)
|
||||
print(f" - {f} ({size_mb:.2f} MB)")
|
||||
|
||||
# Copy the main model file to a simpler location for easy access
|
||||
main_model_file = os.path.join(output_dir, "model.onnx")
|
||||
if os.path.exists(main_model_file):
|
||||
simple_output = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Models", "address-embedding-model.onnx")
|
||||
import shutil
|
||||
shutil.copy(main_model_file, simple_output)
|
||||
print(f"\n[OK] Copied model to: {simple_output}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def create_powershell_download_script():
|
||||
"""Create a PowerShell script for Windows users who can't run Python."""
|
||||
ps_script = '''#!/usr/bin/env pwsh
|
||||
# Download script for custom address embedding model (ONNX format)
|
||||
# This script downloads a pre-converted ONNX model if available
|
||||
|
||||
$ModelRepo = "jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3"
|
||||
$OutputPath = "Models/address-embedding-model.onnx"
|
||||
|
||||
Write-Host "Attempting to download pre-converted ONNX model..." -ForegroundColor Cyan
|
||||
|
||||
# Try to download from HuggingFace Hub (ONNX format if available)
|
||||
$OnnxUrl = "https://huggingface.co/$ModelRepo/resolve/main/onnx/model.onnx"
|
||||
|
||||
try {
|
||||
$ProgressPreference = 'SilentlyContinue'
|
||||
|
||||
if (Get-Command curl -ErrorAction SilentlyContinue) {
|
||||
curl -L -o $OutputPath $OnnxUrl --fail
|
||||
} else {
|
||||
Invoke-WebRequest -Uri $OnnxUrl -OutFile $OutputPath -UseBasicParsing
|
||||
}
|
||||
|
||||
if (Test-Path $OutputPath) {
|
||||
$size = (Get-Item $OutputPath).Length / 1MB
|
||||
Write-Host "Successfully downloaded model to $OutputPath" -ForegroundColor Green
|
||||
Write-Host "File size: $([math]::Round($size, 2)) MB" -ForegroundColor Gray
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "Could not download pre-converted ONNX model." -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "The model does not have a pre-converted ONNX format." -ForegroundColor White
|
||||
Write-Host "Please run the Python conversion script instead:" -ForegroundColor White
|
||||
Write-Host " python download-convert-model.py" -ForegroundColor Gray
|
||||
exit 1
|
||||
}
|
||||
'''
|
||||
|
||||
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Models", "download-custom-model.ps1")
|
||||
with open(script_path, "w") as f:
|
||||
f.write(ps_script)
|
||||
print(f"\n[OK] Created PowerShell fallback script: {script_path}")
|
||||
|
||||
def main():
|
||||
print("\n" + "="*60)
|
||||
print("Custom Address Embedding Model - ONNX Converter")
|
||||
print("="*60)
|
||||
|
||||
# Check if we should just create the fallback script
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--check-only":
|
||||
check_requirements()
|
||||
return 0
|
||||
|
||||
# Check requirements first
|
||||
print("Checking requirements...")
|
||||
check_requirements()
|
||||
|
||||
# Download and convert
|
||||
if download_and_convert_model():
|
||||
# Create fallback PowerShell script
|
||||
create_powershell_download_script()
|
||||
print("\n" + "="*60)
|
||||
print("Conversion complete!")
|
||||
print("="*60)
|
||||
print("\nNext steps:")
|
||||
print("1. Update appsettings.json to use the new model:")
|
||||
print(' "Embedding": { "ModelName": "custom-all-MiniLM-L6-v2-address" }')
|
||||
print("2. Update EmbeddingService.cs to support the new model path")
|
||||
print("3. Run the application")
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -93,17 +93,29 @@ public class EmbeddingService : IEmbeddingService
|
||||
var modelFileName = modelName switch
|
||||
{
|
||||
"sentence-transformers/all-MiniLM-L6-v2" => "all-MiniLM-L6-v2.onnx",
|
||||
"jarredparrett/all-MiniLM-L6-v2_tuned_on_deepparse_address_mutations_comb_3" => "address-embedding-model.onnx",
|
||||
"custom-all-MiniLM-L6-v2-address" => "address-embedding-model.onnx",
|
||||
_ => throw new NotSupportedException($"Model '{modelName}' is not supported for local inference")
|
||||
};
|
||||
|
||||
var modelPath = Path.Combine(AppContext.BaseDirectory, "Models", modelFileName);
|
||||
|
||||
if (!File.Exists(modelPath))
|
||||
// Check multiple possible locations for the model file
|
||||
var possiblePaths = new[]
|
||||
{
|
||||
modelPath = Path.Combine("Models", modelFileName);
|
||||
Path.Combine(AppContext.BaseDirectory, "Models", modelFileName),
|
||||
Path.Combine(AppContext.BaseDirectory, "Models", "Models", modelFileName),
|
||||
Path.Combine("Models", modelFileName),
|
||||
Path.Combine("Models", "Models", modelFileName)
|
||||
};
|
||||
|
||||
foreach (var modelPath in possiblePaths)
|
||||
{
|
||||
if (File.Exists(modelPath))
|
||||
{
|
||||
return modelPath;
|
||||
}
|
||||
}
|
||||
|
||||
return modelPath;
|
||||
throw new FileNotFoundException($"Model file '{modelFileName}' not found. Searched in: {string.Join(", ", possiblePaths)}");
|
||||
}
|
||||
|
||||
public Task<float[]> GenerateEmbeddingAsync(string text, CancellationToken cancellationToken = default)
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<None Update="Models\all-MiniLM-L6-v2.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Models\Models\address-embedding-model.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"CollectionName": "addresses"
|
||||
},
|
||||
"Embedding": {
|
||||
"ModelName": "sentence-transformers/all-MiniLM-L6-v2",
|
||||
"ModelName": "custom-all-MiniLM-L6-v2-address",
|
||||
"Dimension": 384,
|
||||
"ApiToken": "",
|
||||
"UseLocalInference": true
|
||||
|
||||
Reference in New Issue
Block a user