現在很多做人臉識別的技術都是基於C++,python;但是java也是有自己的深度學習的庫,Deeplearning4j就是java自己的深度學習庫,ND4j就是dp4j裏面專門做向量計算的庫,現在就用ND4j實現1:N矩陣計算功能:
pom:
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>nd4j-native</artifactId>
<version>${dp4j-version}</version>
</dependency>
1:N計算的核心就是,用矩陣和一個大矩陣做內積乘法
業務功能:就是用一張人臉的特徵和數據庫裏面有的特徵做內積乘法,快速在數據庫裏面找到這張人臉對應的人;
現實方法:
由於每次都在數據庫裏面去查詢特徵數據會消耗時間,所以我把特徵數據初始化加載到內存裏面來維護;做完內積計算以後需要找到相識度最大的對應的那條數據,opencv裏面有一個sortIndex函數可以將數組的值排序以後返回對應的索引地址,java可以自己實現一個排序;
將查出來的特徵值進行緩存:
@Component
public class NDCache {
public static List<BasisFeature> basisFeatures = Lists.newArrayList();
public static INDArray zeros = null;
public static void load(List<BasisFeature> features){
basisFeatures = features;
List<INDArray> INDArrays = features.stream().map(basisFeature -> Nd4j.create(basisFeature.getBasis())).collect(Collectors.toList());
zeros = Nd4j.vstack(INDArrays);
}
}
實現矩陣計算,更新矩陣:
@Slf4j
@Service
public class NDService {
@Autowired
private MongoTemplate mongoTemplate;
private static double forecastValues = 0.65D;
private INDArray creArry(List<Double> features) {
INDArray array = Nd4j.create(features.size(), 1);
features.forEach(item -> array.putRow(features.indexOf(item), Nd4j.create(new double[]{item})));
return array;
}
public BasisFeature comparRes(List<Double> features){
INDArray resArr = NDCache.zeros.mmul(creArry(features));
Point[] points = Point.sortPoint(resArr.toDoubleVector());
Point point = points[0];
if (point.getValue() < forecastValues){
return null;
}
log.info("1比N第一位結果:"+point);
return NDCache.basisFeatures.get(point.getIndex());
}
public void updateArr(BasisFeature basisFeature){
for (int i=0;i<NDCache.basisFeatures.size();i++){
BasisFeature feature = NDCache.basisFeatures.get(i);
if (feature.getObjectId().equals(basisFeature.getObjectId())){
NDCache.basisFeatures.set(i,basisFeature);
NDCache.zeros.put(i,Nd4j.create(basisFeature.getBasis()));
log.info("矩陣更新:"+NDCache.zeros.length());
}
}
}
public void loadNDarr(){
Query query = new Query();
List<BasisFeature> features = mongoTemplate.find(query,BasisFeature.class);
NDCache.basisFeatures = features;
List<INDArray> INDArrays = features.stream().map(basisFeature -> Nd4j.create(basisFeature.getBasis())).collect(Collectors.toList());
NDCache.zeros = Nd4j.vstack(INDArrays);
log.info("初始化矩陣大小:"+NDCache.zeros.length()+":數據源大小:"+features.size());
}
public void add(BasisFeature ... basisFeatures){
if (ObjectUtils.isEmpty(NDCache.zeros)){
loadNDarr();
}else {
List<INDArray> indArrays = Lists.newArrayList(NDCache.zeros);
Arrays.asList(basisFeatures).stream().forEach(basisFeature -> indArrays.add(Nd4j.create(basisFeature.getBasis())));
NDCache.zeros = Nd4j.vstack(indArrays);
log.info("矩陣擴張:"+NDCache.zeros.length());
}
}
}
自定義sortIndex:
public class SortComprator implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
Point t1=(Point)arg0;
Point t2=(Point)arg1;
return t2.getValue().compareTo(t1.getValue());
}
}
整個計算流程只是把其他語言的實現方法實現了一遍,但是java在計算方便的速度也是很快的,一百萬的矩陣差不多也就141ms,
這個只是基於cpu的計算,當然nd4j也是可以基於cuda的GPU計算的。