Committing too many files but the app runs possibly with a new model.

This commit is contained in:
2026-01-16 23:07:45 -05:00
parent 12fd2ef45e
commit cb4488ee58
14 changed files with 61686 additions and 6 deletions
@@ -21,6 +21,14 @@ public class EmbeddingConfiguration
public int Dimension { get; set; } = 384; public int Dimension { get; set; } = 384;
public string ApiToken { get; set; } = string.Empty; public string ApiToken { get; set; } = string.Empty;
public bool UseLocalInference { get; set; } = true; 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 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.
@@ -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
}
@@ -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())
+17 -5
View File
@@ -93,17 +93,29 @@ public class EmbeddingService : IEmbeddingService
var modelFileName = modelName switch var modelFileName = modelName switch
{ {
"sentence-transformers/all-MiniLM-L6-v2" => "all-MiniLM-L6-v2.onnx", "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") _ => throw new NotSupportedException($"Model '{modelName}' is not supported for local inference")
}; };
var modelPath = Path.Combine(AppContext.BaseDirectory, "Models", modelFileName); // Check multiple possible locations for the model file
var possiblePaths = new[]
if (!File.Exists(modelPath))
{ {
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) public Task<float[]> GenerateEmbeddingAsync(string text, CancellationToken cancellationToken = default)
+3
View File
@@ -21,6 +21,9 @@
<None Update="Models\all-MiniLM-L6-v2.onnx"> <None Update="Models\all-MiniLM-L6-v2.onnx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Models\Models\address-embedding-model.onnx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>
+1 -1
View File
@@ -6,7 +6,7 @@
"CollectionName": "addresses" "CollectionName": "addresses"
}, },
"Embedding": { "Embedding": {
"ModelName": "sentence-transformers/all-MiniLM-L6-v2", "ModelName": "custom-all-MiniLM-L6-v2-address",
"Dimension": 384, "Dimension": 384,
"ApiToken": "", "ApiToken": "",
"UseLocalInference": true "UseLocalInference": true