What's your use case?
I am using the kNN widget in Orange for text classification tasks based on document embeddings (e.g., from the Document Embedding widget).
In this context, similarity between instances is typically measured using cosine similarity. However, since cosine distance is not available in the kNN widget, a common workaround is to use Euclidean distance on L2-normalized vectors, which is mathematically equivalent in terms of neighbor ranking. Source Bouhsine, T. (2026, febrero 23). In Defense of Cosine Similarity: Normalization Eliminates the Gauge Freedom: "3. Euclidean k-NN on normalized embeddings = cosine search. By Corollary 5, FAISS flat L2 search on unit-normalized vectors produces the same neighbor rankings as cosine search. Use whichever is faster for your infrastructure."
Currently, Orange does not provide an easy way to apply L2 normalization per instance within the standard workflow. While it is possible to implement this via a Python Script widget, this introduces additional complexity and breaks the simplicity and reproducibility of the pipeline, especially for non-technical users.
Moreover, applying normalization externally via the Preprocess widget overrides the learner’s internal preprocessing steps, which can lead to unintended side effects.
What's your proposed solution?
Add an optional checkbox in the kNN widget:
Normalize instances (L2)
This option should:
apply L2 normalization per instance before distance computation
be enabled only when the Euclidean metric is selected
be integrated into the learner’s internal preprocessing pipeline (without overriding existing preprocessing steps)
This would allow users to effectively use cosine-equivalent similarity while preserving the current behavior and optimizations of the kNN implementation.
Are there any alternative solutions?
Yes, but they are less ideal:
1.- Using a Python Script widget
· requires custom code
· reduces usability for non-programmers
· makes workflows less transparent
2.- Adding L2 normalization in the Preprocess widget
· applies globally rather than model-specific
· overrides the learner’s internal preprocessing
· may affect other models unintentionally
3.- Adding cosine distance directly to kNN
· would require switching to brute-force search in many cases
· may negatively impact performance
· adds complexity to the implementation
For these reasons, integrating L2 normalization as an optional feature within the kNN widget seems to be the most practical and user-friendly solution.
What's your use case?
I am using the kNN widget in Orange for text classification tasks based on document embeddings (e.g., from the Document Embedding widget).
In this context, similarity between instances is typically measured using cosine similarity. However, since cosine distance is not available in the kNN widget, a common workaround is to use Euclidean distance on L2-normalized vectors, which is mathematically equivalent in terms of neighbor ranking. Source Bouhsine, T. (2026, febrero 23). In Defense of Cosine Similarity: Normalization Eliminates the Gauge Freedom: "3. Euclidean k-NN on normalized embeddings = cosine search. By Corollary 5, FAISS flat L2 search on unit-normalized vectors produces the same neighbor rankings as cosine search. Use whichever is faster for your infrastructure."
Currently, Orange does not provide an easy way to apply L2 normalization per instance within the standard workflow. While it is possible to implement this via a Python Script widget, this introduces additional complexity and breaks the simplicity and reproducibility of the pipeline, especially for non-technical users.
Moreover, applying normalization externally via the Preprocess widget overrides the learner’s internal preprocessing steps, which can lead to unintended side effects.
What's your proposed solution?
Add an optional checkbox in the kNN widget:
Normalize instances (L2)
This option should:
apply L2 normalization per instance before distance computation
be enabled only when the Euclidean metric is selected
be integrated into the learner’s internal preprocessing pipeline (without overriding existing preprocessing steps)
This would allow users to effectively use cosine-equivalent similarity while preserving the current behavior and optimizations of the kNN implementation.
Are there any alternative solutions?
Yes, but they are less ideal:
1.- Using a Python Script widget
· requires custom code
· reduces usability for non-programmers
· makes workflows less transparent
2.- Adding L2 normalization in the Preprocess widget
· applies globally rather than model-specific
· overrides the learner’s internal preprocessing
· may affect other models unintentionally
3.- Adding cosine distance directly to kNN
· would require switching to brute-force search in many cases
· may negatively impact performance
· adds complexity to the implementation
For these reasons, integrating L2 normalization as an optional feature within the kNN widget seems to be the most practical and user-friendly solution.