Show HN: Semantic Grep – A Word2Vec-powered search tool (github.com)
Much improved new version. Search for words similar to the query. For example, "death" will find "death", "dying", "dead", "killing"... Incredibly useful for exploring large text datasets where exact matches are too restrictive.
57 comments
[ 7.3 ms ] story [ 121 ms ] threadHaving no experience with word2vec, some reference performance numbers would be great. If I have one million PDF pages, how long is that going to take to encode? How long will it take to search? Is it CPU only or will I get a huge performance benefit if I have a GPU?
But there just isn’t anything comparable when it comes to building your own search engine. Postgres with a vector extension is good if all you want to do is a vector search and some SQL (not dismissing it here, I love PG); but if you want more complex search cases, Elasticsearch is the way to go.
Well, Apple did experiment with embeddings for next word prediction on iOS in 2018 (apparently, they also used it for many other features)! https://machinelearning.apple.com/research/can-global-semant... / https://archive.is/1oCwr
Do I understand correctly that this works by splitting each line into words, and using the embedding for each word?
I wonder whether it might be feasible to search by semantics of longer sequences of text, using some language model (like, one of the smaller ones, like GPT2-small or something?). Like, so that if you were searching for “die”, then “kick the bucket” and “buy the farm”, could also match somehow? Though, I’m not sure what vector you would use to do the dot product with, when there is a sequence of tokens, each with associated key vectors for each head at each layer, rather than a single vector associated with a word.. Maybe one of the encoder-decoder models rather than the decoder only models?
Though, for things like grep, one probably wants things to be very fast and as lightweight as feasible, which I imagine is much more the case with word vectors (as you have here) than it would be using a whole transformer model to produce the vectors.
Maybe if one wanted to catch words that aren’t separated correctly, one could detect if the line isn’t comprised of well-separated words, and if so, find all words that appear as a substring of that line? Though maybe that would be too slow?
Are models like mistral there yet in terms of token per second generation to run a grep over millions of files?
[0] https://simonwillison.net/2023/Oct/23/embeddings/
The configuration thing is unclear to me. I think that "current directory" means "same directory as the binary", but it could mean pwd.
Neither of those is good: configuration doesn't belong where the binaries go, and it's obviously wrong to look for configs in the working directory.
I suggest checking $XDG_CONFIG_HOME, and defaulting to `~/.config/sgrep/config.toml`.
That extension is not a typo, btw. JSON is unpleasant to edit for configuration purposes, TOML is not.
Or you could use an ENV variable directly, if the only thing that needs configuring is the model's location, that would be fine as well.
If that were the on ramp, I'd be giving feedback on the program instead. I do think it's a clever idea and I'd like to try it out.
It took me a few tries to get this to run. I tried passing -model_path, but it complained that it couldn't find config.json. I then made a config.json in the pwd (which contained the executable), but it couldn't expand "~". I then tried searching *.txt, but it couldn't find anything unless I specified just one file.
In terms of offering an experience similar to grep, it's a lot slower. It's not quite as slow with the SLIM file, but I wonder if there's some low-hanging fruit for optimization. Perhaps caching similarity for input tokens?
Just in general, semantic search across text seems like a much better ux for many applications. Wish it was more prevalent.
Like grep but for natural language questions. Based on Mistral LLMs.
Alas the word2vec repository has reached its quota:
So here are another sources I found for it: https://stackoverflow.com/a/43423646I also found https://huggingface.co/fse/word2vec-google-news-300/tree/mai... but I'm unsure if that's the correct format for this tool. The first source from Google Drive seems to work and there's little chance of being malicious..
Haven't tried the huggingface model, but, looks very different. Unlikely to work.
https://github.com/arunsupe/semantic-grep/blob/b7dcc82a7cbab...
You can read the vector all at once. See e.g.:
https://github.com/danieldk/go2vec/blob/ee0e8720a8f518315f35...
---
https://github.com/arunsupe/semantic-grep/blob/b7dcc82a7cbab...
You can compute the similarity much faster by using BLAS. Good BLAS libraries have SIMD-optimized implementations. Or if you do multiple tokens as once, you can do a matrix-vector multiplication (sgemv), which will be even faster in many implementations. Alternatively, there is probably also a SIMD implementation in Go using assembly (it has been 7 years since I looked at anything in the Go ecosystem).
You could also normalize the vectors while loading. Then during runtime the cosine similarity is just the dot product of the vectors (whether it pays off depends on the size of your embedding matrix and the size of the haystack that you are going to search).
C#: https://github.com/dotnet/runtime/blob/main/docs/coding-guid... (and full family of other types: Vector2/3/4, Matrix3x2/4x4 and upcoming Tensor<T>), vector similarity I was talking about is this: https://learn.microsoft.com/en-us/dotnet/api/system.numerics..., it uses a highly optimized SIMD kernel, for DotProduct just use an adjacent method
Swift: https://developer.apple.com/documentation/swift/simd-vector-... it is also a competent language at portable SIMD by virtue of using LLVM and offering almost the same operators-based API (e.g. masked = vec1 & ~vec2) like C#
Mojo: https://docs.modular.com/mojo/stdlib/builtin/simd which follows the above two, it too targets LLVM so expect good SIMD codegen as long the lowering strategy does it in an LLVM-friendly way, which I have not looked at yet.
Mojo team's blog posts do indicate they care about optimal compiler output, and it seems to have ambitions for a wider domain of application which is why it is mentioned.
vector search is the first step of the process, and i argue that getting top n results and letting the user process them is probably the best of both worlds without involving "AI".
this can be enhanced with multilingual support based on the encoder used. but building the index is still an expensive process and i wonder how that could be done fast for a local user.
Curious would it handle negation of trained keywords, e.g "not urgent"?
To summarize, this is a simple implementation that works for the simplest use case - semantic matching of words.
- check to see if the search query contains more than one word
- spin up a more modestly sized LLM, such as mistral 7b
- ask the LLM to try to condense / find single word synonym for the user query
- send to sgrep
Something like SBERT addresses this.
For the drawbacks:
Word embeddings are only good at similarity search style queries - stuff like paraphrasing.
Negation they'll necessarily struggle with. Since word embeddings are generally summed or averaged into a sentence embedding, a negation won't shift the sentence vector space around the way it would in a LM embedding.
Also things like homonyms are issues, but this is massively overblown as a reason to use LM embeddings (at least for latin/germanic languages).
Most people use LM embeddings because they've been told it's the best thing by other people rather than benchmarking accuracy and performance for their usecase.
1. https://github.com/oborchers/Fast_Sentence_Embeddings
2. https://www.sbert.net/docs/sentence_transformer/pretrained_m...