iOS:SwiftUI TextField文本调用NLEmbedding返回nil问题求助
SwiftUI中NLEmbedding.vector(for:)接收@State变量时返回nil的解决办法
我正在开发一款类似苹果键盘的表情搜索栏:用户输入文本后,系统会匹配预计算好并存在.mlmodel里的表情静态word embedding,进而展示相关表情。但遇到个问题:当用SwiftUI的@State变量作为参数调用NLEmbedding.vector(for:)时,返回值总是nil,换成字符串字面量却完全正常。
最小复现代码如下:
import SwiftUI import NaturalLanguage struct ContentView: View { @State private var searchText = "" var body: some View { VStack { TextField("Search emojis", text: $searchText) .padding() .background(Color.gray.opacity(0.1)) .cornerRadius(12) .onChange(of: searchText, perform: searchEmojis(forQuery:)) } .padding() } private func searchEmojis(forQuery text: String) { guard let embedding = NLEmbedding.wordEmbedding(for: .english) else { return } let queryVector = embedding.vector(for: text) // 传字符串字面量正常,传@State变量返回nil print("Query vector: \(queryVector)") } }
解决思路
问题根源是用户输入的文本没有经过标准化处理,而NLEmbedding的预训练词嵌入只包含符合规范的词汇(比如小写、无多余空格的单个词)。比如用户输入带空格的短语、大写字母或者空字符串时,都会找不到对应的向量。
可以通过以下步骤处理:
- 先对输入文本做标准化:去除首尾空格、转成小写
- 处理多词输入:如果用户输入多个词,计算所有词向量的平均值作为查询向量(单个词不在词汇表时也能 fallback)
- 提前判断词汇是否在嵌入模型的词汇表中,避免无效计算
修改后的代码
import SwiftUI import NaturalLanguage struct ContentView: View { @State private var searchText = "" var body: some View { VStack { TextField("Search emojis", text: $searchText) .padding() .background(Color.gray.opacity(0.1)) .cornerRadius(12) .onChange(of: searchText, perform: searchEmojis(forQuery:)) } .padding() } private func searchEmojis(forQuery text: String) { guard let embedding = NLEmbedding.wordEmbedding(for: .english) else { return } // 1. 标准化输入文本 let normalizedText = text.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() guard !normalizedText.isEmpty else { print("Empty input, skipping") return } // 2. 过滤出词汇表中存在的有效单词 let words = normalizedText.components(separatedBy: .whitespaces) .filter { !$0.isEmpty && embedding.vocabulary.contains($0) } guard !words.isEmpty else { print("No valid words in input for embedding") return } // 3. 计算查询向量:单个词直接取向量,多词取平均值 var queryVector: [Double] if words.count == 1 { queryVector = embedding.vector(for: words[0])! } else { let vectors = words.compactMap { embedding.vector(for: $0) } let count = vectors.count queryVector = Array(repeating: 0.0, count: embedding.dimension) for vector in vectors { for i in 0..<embedding.dimension { queryVector[i] += vector[i] / Double(count) } } } print("Query vector: \(queryVector)") // 这里可以继续写和预存表情embedding匹配的逻辑 } }
额外说明
- 预训练的NLEmbedding词汇表有限,对于生僻词或者组合词可能还是找不到向量,这时候可以考虑添加自定义词汇,或者用更灵活的嵌入模型
- 如果你的
.mlmodel里的表情embedding是用相同的预处理规则生成的,这样匹配出来的结果会更准确
内容的提问来源于stack exchange,提问作者Siddharth Kamaria
相关产品推荐
相关产品推荐

