机器学习-KNN算法原理、实战详细讲解(C++/Python两种语言实现)

一、KNN算法讲解

1.1实现环境

C++:集成开发环境VS2022,可视化库matplot++
python:集成开发环境Pycharm,数学库numpy,可视化库matplotlib

1.2KNN算法原理

KNN(K - 近邻算法)是一种监督学习中的惰性学习算法,核心是 “相似样本在特征空间中彼此靠近”,通过计算新样本与训练样本的距离找 k 个最近邻,再用多数表决(分类)或均值(回归)做预测,无需显式训练模型。

1.3算法的优点

(1)原理简单直观,实现难度低,无需复杂参数训练。

(2)对异常值不敏感,适合类域交叉或重叠较多的数据集。

(3)适配多分类任务,且可灵活切换分类与回归场景。

1.4算法的缺点

(1)预测阶段计算量大,时间与空间复杂度高,数据量大时效率低。

(2)对高维数据敏感,易出现 “维度灾难”,需通过降维等手段优化。

(3)样本不平衡时,多数类可能主导预测结果,需做样本均衡处理。

二、评估标准

2.1用ROC曲线评估

ROC 曲线是二分类模型的性能评估曲线,以假正率(FPR) 为横轴、真正率(TPR) 为纵轴,通过遍历分类模型的分类阈值,绘制出不同阈值下模型的 TPR 与 FPR 对应点并连接成线,直观反映模型在 “识别正例” 和 “避免误判负例” 之间的权衡能力(FPR值是所有反例被错误当作正例比例,TPR值是所有正例正确识别出来正例比例;模型正确识别正例的能力越高、错误识别正例的能力越低,这个模型的分类能力就越强)。

2.2计算FPR、TPR

这里涉及到四个数据:TP(真正例)、FP(假正例)、FN(假反例)、TN(真反例)

TP:真实正例被模型正确预测为正例;

FP:真实负例被模型错误预测为正例(误检);

FN:真实正例被模型错误预测为负例(漏检);

TN:真实负例被模型正确预测为负例。

TPR计算公式:

FPR计算公式:

2.3AUC

AUC(ROC 曲线下的面积) 是对 ROC 曲线的量化评估,也是比 ROC 曲线更常用的模型性能指标,取值范围为0~1。AUC的值越接近1,表明模型越完美,分类性能越好。

三、例题分析

3.1问题

海伦一直使用在线约会网站寻找适合自己的约会对象。她曾交往过三种类型的人:

(1)不喜欢的人

(2)一般喜欢的人

(3)非常喜欢的人

这些人包含以下三种特征:

(1)每年获得的飞行常客里程数

(2)玩视频游戏所耗时间百分比

(3)每周消费的冰淇淋公升数

该网站现在需要尽可能向海伦推荐她喜欢的人,需要我们设计一个分类器,根据用户的以上三种特征,识别出是否该向海伦推荐。

3.2数据集类型

每年获得的飞行常客里程数玩视频游戏所耗时间百分比每周消费的冰淇淋的公升数样本分类
14000.80.51
2134000120.93
32000001.12
432000670.12

3.3问题剖析

题目给出了三个特征维度,“每年获得的飞行常客里程数”这一特征维度的数据比另外两个大了2-3个数量级,我们选择了欧几里得距离作为距离标准,所以需要对数据进行预处理,让每个特征维度的权重基本一致。我们需要在导入图中数据时,将这些字符串转换为整型数据,方便使用。

3.4具体实现

3.4.1大致流程示意图

3.4.2具体步骤

(1)数据预处理

归一化将所有数据按原范围大小,均匀压缩到0-1的范围内,如25、50、75、125,原范围25-125,对应0-1范围0、0.25、0.50、1。

给出公式:

洗牌:数据集的数据可能呈现出部分特征集中在某一特定位置,我们需要搅动它让数据均匀地分布在每个地方。对于数据集而言,这种数据集集中分布的情况是会影响生成的模型的能力的,所以我们需要把原来的数据集打乱,让某些过于集中的特征分散开来,提高模型预测的准确性。

(2)最佳K值的选择

k的选取是很重要的,常见的选择方法有经验值法(直接设置参数)和交叉验证法(网格搜索+交叉验证)。

在计距离时用到了欧几里得距离距离计算公式

欧几里得距离计算公式(x和y是同一特征维度的,i表示不同的特征维度):

(3)计算FPR和TPR、AUC并可视化ROC曲线

上面在介绍ROC曲线时已经讲过了,这里就不多说了。

四、C++实现

4.1环境准备

本次我们只需要一个额外的可视化第三方库,我们选择matplot++

matplot++下载

官方链接:matplot++发行版

 VS2022环境搭建

把matplot++放到自己建立的解决方案的其中一个项目的目录下,matplot++的两个静态库,可根据实际位置调整,一般都是一个在lib里,一个在lib\Matplot++里。然后在链接器中链接:
nodesoup.lib
matplot.lib

链接完成后点击确定,到这里环境就配置好了。

4.2代码实现

4.2.1头文件

#include <iostream> #include <fstream> #include <vector> #include <memory> #include <functional> #include <algorithm> #include <unordered_map> #include <random> #include <numeric> #include <queue> #include <matplot/matplot.h>

4.2.2结构体

struct Data { double length; double game_time; double ice_crime_eating; int type; };

这里Data结构体的前三个成员均为double类型而不是用vector,这是因为vector是将数据分散存储到堆区,类似链表结构体,后续我们需要频繁地访问这些数据,需要不停使用指针访问内存,会让访问效率下降。

4.2.3导入数据

std::vector<Data> loadData(const std::string& filepath) { std::ifstream ifs; ifs.open(filepath, std::ios::in); if (!ifs.is_open()) throw std::invalid_argument("Failed to open file: " + filepath); std::vector<Data> datalist; double trail, game, eating; std::string tag; std::vector<std::string> searchlist = { "largeDoses", "smallDoses", "didntLike" }; while (ifs >> trail >> game >> eating >> tag) { int type = -1; for (int i = 0; i < searchlist.size(); ++i) { if (searchlist[i] == tag) { type = i; break; } } datalist.push_back({ trail, game, eating, type }); } ifs.close(); return datalist; }

4.2.4数据预处理

使用lamda来简化三个特征维度的归一化实现:

// 更新归一化数据 void calculate(std::vector<double>& feature) { double max = *max_element(feature.begin(), feature.end()); double min = *min_element(feature.begin(), feature.end()); double range = max - min; for (double& val : feature) val = (val - min) / range; } // 归一化 void normalized(std::vector<Data>& data) { if (data.empty()) { std::cout << "nullptr doesn't normalized!" << std::endl; __debugbreak(); return; } std::vector<std::pair< std::function<double(const Data&)>, std::function<void(Data&, double)> >> field = { { [](const Data& d) {return d.game_time; }, [](Data& d, double val) {d.game_time = val; } }, { [](const Data& d) {return d.ice_crime_eating; }, [](Data& d, double val) {d.ice_crime_eating = val; } }, { [](const Data& d) {return d.length; }, [](Data& d, double val) {d.length = val; } } }; for (auto& f : field) { auto& getVal = f.first; auto& setVal = f.second; std::vector<double> values; for (const auto& d : data) values.push_back(getVal(d)); calculate(values); for (int i = 0; i < data.size(); ++i) setVal(data[i], values[i]); } }

随机生成伪随机数种子,使用shuffle洗牌:

// 打乱数据 std::vector<Data> shuffleData(std::vector<Data>& data) { auto shuffle_data = std::vector<Data>(data.begin(), data.end()); std::random_device rd; std::mt19937 g(rd()); std::shuffle(shuffle_data.begin(), shuffle_data.end(), g); return shuffle_data; }

4.2.5选择最佳K值

 计算欧几里得距离:

// 计算欧几里得距离 double eucliDistance(Data x, Data y) { double trail = x.length - y.length; double play = x.game_time - y.game_time; double eating = x.ice_crime_eating - y.ice_crime_eating; return trail * trail + play * play + eating * eating; }

获取邻居各类别的数量:

std::vector<int> getKNeighborProb(const Data& test, const std::vector<Data>& trains, int k) { if (trains.empty() || k <= 0) throw std::invalid_argument("Invalid input for getNeighborProb!"); std::priority_queue<std::pair<double, int>> maxHeap; // 使用最大堆存储 for (const auto& d : trains) { double dist = eucliDistance(test, d); if (maxHeap.size() < k) maxHeap.emplace(dist, d.type); else if (maxHeap.top().first > dist) { maxHeap.pop(); maxHeap.emplace(dist, d.type); } } std::vector<int> neighborProb(3, 0); while (!maxHeap.empty()) { neighborProb[maxHeap.top().second]++; maxHeap.pop(); } return neighborProb; }

最大的k个邻居与他们的位置无关,只需要使用最大堆把最大的k个邻居和它们的类别存起来,统计最大堆前k个最大邻居出现的类别的数量即可。

选择出现最多的类别作为预测结果:

// 预测类型 int predicType(const Data& test, std::vector<Data>& data, int k) { auto vote = getKNeighborProb(test, data, k); return max_element(vote.begin(), vote.end()) - vote.begin(); }

4.2.6使用k折交叉验证计算准确率和三分类混淆矩阵

// 使用K折交叉验证计算准确率 std::pair<double, std::vector<std::vector<int>>> kFoldCrossVaild(std::vector<Data>& shuffle_data, int kfold, int knn_k) { if (shuffle_data.empty()) throw std::invalid_argument("Invalid input for countRoc: Null data is invaild!"); if (kfold <= 0) throw std::invalid_argument("Invalid input for countRoc: kfold must be bigger than 0!"); std::vector<std::vector<int>> cntlist(3, std::vector<int>(3, 0)); int foldSize = shuffle_data.size() / kfold; std::vector<double> accuracies; for (int fold = 0; fold < kfold; ++fold) { int start = fold * foldSize; int end = (kfold -1 == fold) ? shuffle_data.size() : start + foldSize; int correct = 0; std::vector<Data> trainData(shuffle_data.begin(), shuffle_data.begin() + start); trainData.insert(trainData.end(), shuffle_data.begin() + end, shuffle_data.end()); for(int i = start; i < end; ++i) { auto t = shuffle_data[i]; int res = predicType(t, trainData, knn_k); cntlist[t.type][res]++; correct += (res == t.type); } double accuracy = static_cast<double>(correct) / foldSize; accuracies.push_back(accuracy); } return std::make_pair(std::accumulate(accuracies.begin(), accuracies.end(), 0.0) / kfold , cntlist); }

4.2.7网格搜索

从3开始,跳过偶数(偶数可能会平票,所以步长为2),取数据集总量的开方,减少不必要的计算成本。将得到的准确率和混淆矩阵存起来,跑完网格搜素后寻找最大的准确率,换算k值,返回k和混淆矩阵。

4.2.8计算FPR和TPR

1.遍历vector容器,将所有数据除以k:

// 计算测试集在训练集上的多类型概率 std::vector<double> predicProb(const Data& test, std::vector<Data>& data, int k) { auto vote = getKNeighborProb(test, data, k); std::vector<double> appearance(3, 0); for (int i = 0; i < appearance.size(); ++i) appearance[i] = static_cast<double>(vote[i]) / k; return appearance; }

2.计算某一类型作为正例时单个点的FPR和TPR:

// 使用K折交叉验证,计算ROC图数据 std::pair<double, double> countRoc(std::vector<Data>& shuffle_data, int kfold, int knn_k, double confd, int genre) { if (shuffle_data.empty()) throw std::invalid_argument("Invalid input for countRoc: Null data is invaild!"); if (kfold <= 0) throw std::invalid_argument("Invalid input for countRoc: kfold must be bigger than 0!"); int foldSize = shuffle_data.size() / kfold; int TP = 0, FP = 0, FN = 0, TN = 0; for (int fold = 0; fold < kfold; ++fold) { int start = fold * foldSize; int end = (kfold - 1 == fold) ? shuffle_data.size() : start + foldSize; int correct = 0; std::vector<Data> trainData; trainData.reserve(shuffle_data.size() - foldSize); trainData.assign(shuffle_data.begin(), shuffle_data.begin() + start); trainData.insert(trainData.end(), shuffle_data.begin() + end, shuffle_data.end()); for (int i = start; i < end; ++i) { auto t = shuffle_data[i]; std::vector<double> res = predicProb(t, trainData, knn_k); bool isPred = (res[genre] >= confd); bool isTrue = (genre == t.type); TP += isPred && isTrue; FP += isPred && (!isTrue); FN += (!isPred) && isTrue; TN += (!isPred) && (!isTrue); } } double TPR = ( TP+FN == 0 ? 0.0 : static_cast<double>(TP) / (TP + FN)); double FPR = ( FP+TN == 0 ? 0.0 : static_cast<double>(FP) / (FP + TN)); return { TPR, FPR }; }

3.将某一类别所有点数据整理补全:

// 组织ROC图数据并补全 std::pair<std::vector<double>, std::vector<double>> getROC(std::vector<Data>& data, int kfold, int knn_k, int genre) { std::vector<double> tprlist; std::vector<double> fprlist; for (double confd = 1.0; confd > 0; confd -= 0.05) { auto [tpr, fpr] = countRoc(data, kfold, knn_k, confd, genre); tprlist.push_back(tpr); fprlist.push_back(fpr); } if (!(tprlist.front() == 0 && fprlist.front() == 0)) { tprlist.insert(tprlist.begin(), 0); fprlist.insert(fprlist.begin(), 0); } if (!(tprlist.back() == 1 && fprlist.back() == 1)) { tprlist.push_back(1); fprlist.push_back(1); } return { fprlist, tprlist }; }

由于可能出现FPR不为0和不为1的情况,这样面积就可能会少算一部分(最小FPR的左边区域和最大FPR的右边区域),所以需要补全。

4.2.9计算AUC和可视化实现

// 累加AUC得到最终值 结合8使用 data十分有序,无需处理即可使用 double getAUC(std::pair<std::vector<double>, std::vector<double>>& data) { double auc = 0.0; auto& fprs = data.first; auto& tprs = data.second; for (int i = 0; i < fprs.size()-1; ++i) { auc += (fprs[i + 1] - fprs[i]) * (tprs[i + 1] + tprs[i]) / 2; } std::cout << auc << std::endl; return auc; }
void plotEvalution(std::vector<std::pair<std::vector<double>, std::vector<double>>> data, std::vector<double> aucs, std::vector<std::vector<int>>& list) { using namespace matplot; std::vector<std::string> colorList({ "blue", "yellow", "green", "red" }); std::vector<std::string> tagName({ "largeDoses", "smallDoses", "didntLike", "Random Guess" }); auto fig = figure(true); // 设置为true是能正常使用figure的关键 fig->size(2560, 1000); subplot(1, 2, 1); hold(on); for (int i = 0; i < data.size(); ++i) { plot(data[i].first, data[i].second)->line_width(2).line_style("-").color(colorList[i]); } plot(std::vector{ 0,1 }, std::vector{ 0,1 })->line_width(1).line_style("--").color(colorList[3]); hold(off); xlabel("False Positive Rate"); ylabel("True Positive Rate"); title("ROC"); xlim({ 0, 1 }); ylim({ 0, 1 }); for (int i = 0; i < aucs.size(); ++i) text(0.02, 0.1 - i * 0.025, tagName[i] + " AUC: " + std::to_string(aucs[i]))->font_size(8); auto l = ::matplot::legend(tagName); l->location(legend::general_alignment::bottomright); l->num_rows(2); l->font_size(5); subplot(1, 2, 2); heatmap(list)->normalization(matrix::color_normalization::columns); title("Three distribution"); auto ax = gca(); ax->x_axis().ticklabels({ "largeDoses", "smallDoses" , "didntLike" }); ax->y_axis().ticklabels({ "largeDoses", "smallDoses" , "didntLike" }); ax->x_axis().label_font_size(5); ax->y_axis().label_font_size(5); //save("ROC.png"); show(); }

suplot(1, 2, 1)下面的是ROC曲线可视化
suplot(1, 2, 2)下面的是热力图可视化

最后调用mian函数即可:

int main() { auto data = loadData("source\\datingTestSet.txt"); normalized(data); auto shuffle_Data = shuffleData(data); auto [best_k, list] = getBestK(data); std::vector<std::pair<std::vector<double>, std::vector<double>>> results; std::vector<double> AUCs; for (int i = 0; i < 3; ++i) { auto res = getROC(shuffle_Data, 10, best_k, i); auto auc = getAUC(res); results.push_back(res); AUCs.push_back(auc); } plotEvalution(results, AUCs, list); return 0; }

4.3结果展示

五、python实现

python也需要配置环境,但是python的环境配置简单很多

5.1环境配置

在你的新的虚拟环境中,安装numpy和matplotlib:

conda install numpy conda install matplotlib

matplotlib官方使用说明 
numpy官方使用说明

然后在创建项目时选择conda环境,到你的anaconda的文件夹中找到虚拟环境对应的python.exe即可。

5.2代码实现

import numpy as np import matplotlib.pyplot as plt tag_map = { "largeDoses": 0, "smallDoses": 1, "didntLike": 2 } # 导入数据 def load_data(filepath): # 读取数据 data = [] with open(filepath, 'r', encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue parts = line.split('\t') if len(parts) != 4: print(f"特征数据无法转换:{line}") continue tag = parts[3] data.append({ "feature": [parts[0], parts[1], parts[2]], "tag": tag_map[tag] }) return data # 归一化 利用numpy的广播机制 def normalized(features: np.ndarray): # 获取每一列的最大值和最小值 max_val = features.max(axis=0) min_val = features.min(axis=0) ranges = max_val-min_val ranges[ranges < 1e-9] = 1.0 # 比较每个列的相减结果,排除0的情况 return (features-min_val)/ranges # 计算欧式距离 def euclidean_dist(test, train): return np.sum((test - train) ** 2, axis=1) # 计算各类标签的概率 def predict_prob(test, train_data, k): train_features = train_data[:,:3] dist = euclidean_dist(test, train_features) k_indices = np.argpartition(dist, k)[:k] # 完成一次快速排序 k_tags = train_data[k_indices, 3].astype(int) prob = np.bincount(k_tags, minlength=3) / k return prob # 选择最大概率作为当前测试样本的预测类型 def predict_type(test, train_data, k): return max(enumerate(predict_prob(test, train_data, k)), key=lambda x:x[1])[0] # 使用k折交叉验证,计算准确率和三分类特征混淆矩阵 def k_folds_cross_valid_acc(features: np.ndarray, k, k_fold): # 计算每一折的大小 fold_size = int(len(features)/k_fold) fold_accuracies = 0.0 confusion_mat = np.zeros([3,3], dtype=np.int32) for i in range(k_fold): start = i*fold_size # 防止最后一折不够一折的大小 end = start+fold_size if i != k_fold-1 else len(features) train_data = np.concatenate([features[:start], features[end:]]) correct = 0 test_data = features[start:end] for t in test_data: pred_type = predict_type(t[:3], train_data, k) if pred_type == t[3]: correct += 1 confusion_mat[int(t[3])][pred_type] += 1 fold_accuracies += correct/fold_size return fold_accuracies/k_fold, confusion_mat # 计算模型roc曲线的单个点 def count_roc(features: np.ndarray, k_fold, k_neighbor, confidence, genre): fold_size = int(len(features)/k_fold) tp, tn, fp, fn = 0, 0, 0, 0 for i in range(k_fold): start = i*fold_size end = start+fold_size if i != k_fold-1 else len(features) train_data = np.concatenate([features[:start], features[end:]]) test_data = features[start:end] for t in test_data: prob = predict_prob(t[:3], train_data, k_neighbor) if genre == t[3] and prob[genre] >= confidence: tp += 1 elif genre != t[3] and prob[genre] < confidence: tn += 1 elif genre != t[3] and prob[genre] >= confidence: fp += 1 elif genre == t[3] and prob[genre] < confidence: fn += 1 tpr = tp / (tp+fn) if (tp+fn) else 0.0 fpr = fp / (fp+tn) if (fp+tn) else 0.0 return fpr, tpr # 整理并补全roc曲线的两个点集 def get_roc(features: np.ndarray, k_fold, k_neighbor, genre): # 生成1到0步长为-0.05的列表 confidence = np.arange(1.0, 0, -0.05) fpr_list = list() tpr_list = list() for conf in confidence: fpr, tpr = count_roc(features, k_fold, k_neighbor, conf, genre) fpr_list.append(fpr) tpr_list.append(tpr) if fpr_list[0] != 0.0: fpr_list.insert(0, 0.0) tpr_list.insert(0, 0.0) if fpr_list[len(fpr_list)-1] != 1.0: fpr_list.append(1.0) tpr_list.append(1.0) return [fpr_list, tpr_list] # 获取AUC def get_auc(result): auc = 0.0 fpr_list, tpr_list = result if len(fpr_list) < 2: return 0.0 for i in range(len(fpr_list)-1): # 梯形法算AUC面积 auc += (fpr_list[i+1]-fpr_list[i])*(tpr_list[i]+tpr_list[i+1])/2 return auc # 通过值寻找键列表 def get_value(dictionary: dict, target_value: int): return [key for key, value in dictionary.items() if value == target_value] # 绘制roc曲线 def plot_roc(results, ax): class_names = list(tag_map.keys()) for i in range(len(results)): ax.plot(results[i][0], results[i][1], linewidth=2, label=f"{class_names[i]} AUC: {results[i][2]:0.5f}") ax.plot((0, 1), (0, 1), "--", linewidth=1, label="predicted line") ax.legend(loc="lower right") ax.set_title("ROC Curve") ax.set_xlabel("False Positive Rate") ax.set_ylabel("True Positive Rate") ax.set_xlim(0,1) ax.set_ylim(0,1) # 绘制热力图 def plot_heatmap(heat_conf, ax): class_names = list(tag_map.keys()) im = ax.imshow(heat_conf) ax.set_xticks(range(len(class_names))) ax.set_xticklabels(labels=tag_map.keys(), rotation=45, ha="right", rotation_mode="anchor") ax.set_yticks(range(len(class_names))) ax.set_yticklabels(labels=tag_map.keys()) ax.set_title("Three Distribution") for i in range(len(tag_map)): for j in range(len(tag_map)): value = heat_conf[i, j] # 提高可视化观感 text_color = "black" if im.norm(value) > 0.5 else "white" ax.text(j, i, value, ha="center", va="center", color=text_color, fontweight="bold") cbar = plt.colorbar(im, ax=ax) cbar.set_label("Sample Count") if __name__ == "__main__": # 导入数据 dataset = load_data("datingTestSet.txt") # 检查数据是否导入 if not dataset: print("没有有效数据") exit() # 创建numpy数组 feature_list = np.array([d["feature"] for d in dataset], dtype=np.float64) tag_list = np.array([d["tag"] for d in dataset], dtype=np.int32) # 归一化数组 normalized_feature = normalized(feature_list) # 将特征和标签拼接 tag_list = tag_list.reshape(-1, 1) # 将numpy数组转置 feature_tag = np.hstack((normalized_feature, tag_list)) # 洗牌 np.random.seed(42) np.random.shuffle(feature_tag) # 选择最大准确率,选择最佳k值,并计算出最佳k值下的三分类特征混淆矩阵 best_k = 0 best_acc = 0 best_confusion = np.zeros([3, 3], dtype=np.int32) for i in range(3, int(np.sqrt(len(feature_tag))), 2): acc, confusion = k_folds_cross_valid_acc(feature_tag, i, 10) if best_acc < acc: best_k = i best_acc = acc best_confusion = confusion print(f"十折交叉验证的最佳k值为:{best_k},对应的准确率为:{best_acc:0.5f}") # 评估模型:获取ROC曲线和AUC值 results = list() for i in range(3): res = get_roc(feature_tag, 10, best_k, i) auc = get_auc(res) results.append([*res, auc]) # 评估数据可视化 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) plot_roc(results, ax1) plot_heatmap(best_confusion, ax2) plt.tight_layout() plt.show()

5.3运行结果

Read more

AI+游戏开发:如何用 DeepSeek 打造高性能贪吃蛇游戏

AI+游戏开发:如何用 DeepSeek 打造高性能贪吃蛇游戏

文章目录 * 一、技术选型与准备 * 1.1 传统开发 vs AI生成 * 1.2 环境搭建与工具选择 * 1.3 DeepSeek API 初步体验 * 二、贪吃蛇游戏基础实现 * 2.1 游戏结构设计 * 2.2 初始化游戏 * 2.3 DeepSeek 生成核心逻辑 * 三、游戏功能扩展 * 3.1 多人联机模式 * 3.2 游戏难度动态调整 * 3.3 游戏本地保存与回放 * 3.4 跨平台移植 * 《Vue.js项目开发全程实录/软件项目开发全程实录》 * 编辑推荐 * 内容简介 * 作者简介 * 目录 一、

By Ne0inhk
[DeepSeek] 入门详细指南(上)

[DeepSeek] 入门详细指南(上)

前言 今天的是 zty 写DeepSeek的第1篇文章,这个系列我也不知道能更多久,大约是一周一更吧,然后跟C++的知识详解换着更。 来冲个100赞兄弟们 最近啊,浙江出现了一匹AI界的黑马——DeepSeek。这个名字可能对很多人来说还比较陌生,但它已经在全球范围内引发了巨大的关注,甚至让一些科技巨头感到了压力。简单来说这 DeepSeek足以改变世界格局                                                   先   赞   后   看    养   成   习   惯  众所周知,一篇文章需要一个头图                                                   先   赞   后   看    养   成   习   惯   上面那行字怎么读呢,让大家来跟我一起读一遍吧,先~赞~后~看~养~成~习~惯~ 想要 DeepSeek从入门到精通.pdf 文件的加这个企鹅群:953793685(

By Ne0inhk
DeepFace深度学习库+OpenCV实现——情绪分析器

DeepFace深度学习库+OpenCV实现——情绪分析器

目录 应用场景 实现组件 1. 硬件组件 2. 软件库与依赖 3. 功能模块 代码详解(实现思路) 导入必要的库 打开摄像头并初始化变量 主循环 FPS计算 情绪分析及结果展示 显示FPS和图像 退出条件 编辑 完整代码 效果展示 自然的 开心的 伤心的 恐惧的 惊讶的  效果展示 自然的 开心的 伤心的 恐惧的 惊讶的   应用场景         应用场景比较广泛,尤其是在需要了解和分析人类情感反应的场合。: 1. 心理健康评估:在心理健康领域,可以通过长期监控和分析一个人的情绪变化来辅助医生进行诊断或治疗效果评估。 2. 用户体验研究:在产品设计、广告制作或网站开发过程中,通过观察用户在使用过程中的情绪反应,来优化产品的用户体验。 3. 互动娱乐:在游戏或虚拟现实应用中,根据玩家的情绪状态动态调整游戏难度或故事情节,以增加沉浸感和互动性。

By Ne0inhk
最全java面试题及答案(208道)

最全java面试题及答案(208道)

本文分为十九个模块,分别是:「Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM」 ,如下图所示: 共包含 208 道面试题,本文的宗旨是为读者朋友们整理一份详实而又权威的面试清单,下面一起进入主题吧。 Java 基础 1. JDK 和 JRE 有什么区别? * JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java

By Ne0inhk