分类: Python

  • 部署本地DeepSeek大模型时,怎么选择合适的模型文件?

    问题是什么?

    大家都知道deepseek开源了大模型,可能想本地部署试试效果如何,去各个大模型平台看了一遍,这时候部分人可能会很奇怪,为什么这些平台官方推荐的deepseek大模型都是“DeepSeek-R1-Distill-Qwen” 这种?deepseek和Qwen有什么关系?什么是Distill?

    什么是DeepSeek,Distill,Qwen?

    • DeepSeek-R1,是幻方量化旗下AI公司深度求索(DeepSeek)研发的推理模型 。DeepSeek-R1的强项之一是具有很强的推理能力。
      • 数据搜索能力
    • Qwen,阿里巴巴的大模型,强项是具有较为丰富的参数。
      • 丰富的数据
    • Distill,蒸馏这个过程,就是结合两者的优势,组合成一个新的大模型。
      • 强大的数据集 + 强大的推理计算能力

     

    综上,DeepSeek-R1-Distill-Qwen 类似一种果树的嫁接技术,将deepseek的推理能力嫁接到Qwen的数据里。

    怎么选择适合自己硬件大模型?

    • DeepSeek-R1-Distill-Qwen-32B-IQ3_M.gguf, 14.81GB
    • DeepSeek-R1-Distill-Qwen-7B-f16.gguf, 15.24GB

    以这两个模型为例,它们大小相近,但大多数情况下,2优于1。

    • 1具有320亿参数,IQ3_XS代表了一种平衡性能与效率的量化策略,量化过程可能会导致一定的精度损失,但是它仍然保留了大量的原始信息。
    • 2只有基于70亿参数,没有经过裁剪或量化处理。理论上可以提供最接近于原始训练模型的性能,特别是在准确性和细节处理方面。但是由于参数量较少,它可能在理解和生成复杂文本时不如32B版本。

    这其实是个复杂、需要综合考虑的问题。一般而言,越接近原始大模型的版本最好,但是普通用户受限于显卡算力,只能选择修剪后的版本。

    16GB显存的显卡,一般选择7b,14b参数,跑32b会有压力。因此如果你是最新的5090显卡,就可以更大参数的。

     

     

     

  • 当前文档词语A出现1000次, 词语B出现10次, 词A的置信度一定比B更高吗?

    # 前言

    TFIDF值不仅仅取决于一个词在当前文档中的出现次数(即词频TF),还受到该词在整个文档集合中的分布情况(即逆文档频率IDF)的影响。具体来说,TFIDF值由两个部分组成:

    1. **词频 (Term Frequency, TF)**:这个词在当前文档中出现的频率。
    2. **逆文档频率 (Inverse Document Frequency, IDF)**:这个词在整个文档集合中的稀缺性。

    # 词频 (TF)

     

    **定义**:某个词在当前文档中出现的次数除以文档中总词数。
    **公式**:
    \[ \text{TF}(t) = \frac{\text{词t在文档中出现的次数}}{\text{文档中总词数}} \]

    # 逆文档频率 (IDF)

    **定义**:衡量一个词在整个文档集合中的普遍重要性。如果一个词在很多文档中都出现,那么它的IDF值会较低;反之,如果一个词只在少数文档中出现,那么它的IDF值会较高。
    **公式**:
    \[ \text{IDF}(t) = \log\left(\frac{\text{总文档数}}{\text{包含词t的文档数} + 1}\right) \]

    # TFIDF值

    **定义**:将词频和逆文档频率结合起来,计算出一个词在当前文档中的重要性。
    **公式**:
    \[ \text{TFIDF}(t) = \text{TF}(t) \times \text{IDF}(t) \]

    # 例子

    假设我们有两个词 `a` 和 `b`,它们在当前文档中的出现次数如下:

    词 `a` 在当前文档中出现10次。
    词 `b` 在当前文档中出现1000次。

    但是,在整个文档集合中,这两个词的分布情况不同:

    词 `a` 出现在大多数文档中(例如80%的文档)。
    词 `b` 只出现在少数文档中(例如5%的文档)。

    ## 计算过程

    假设总文档数为1000个文档:

    **词 `a` 的IDF**:
    \[ \text{IDF}(a) = \log\left(\frac{1000}{0.8 \times 1000 + 1}\right) \approx \log(1.25) \approx 0.0969 \]

    **词 `b` 的IDF**:
    \[ \text{IDF}(b) = \log\left(\frac{1000}{0.05 \times 1000 + 1}\right) \approx \log(19.6078) \approx 1.292 \]

    假设当前文档中有1000个词:

    **词 `a` 的TF**:
    \[ \text{TF}(a) = \frac{10}{1000} = 0.01 \]

    **词 `b` 的TF**:
    \[ \text{TF}(b) = \frac{1000}{1000} = 1.0 \]

    ## 计算TFIDF值

    **词 `a` 的TFIDF**:
    \[ \text{TFIDF}(a) = 0.01 \times 0.0969 \approx 0.000969 \]

    **词 `b` 的TFIDF**:
    \[ \text{TFIDF}(b) = 1.0 \times 1.292 \approx 1.292 \]

    # 结论

    • 即使词 `a` 在当前文档中出现次数较少(10次),但由于它在大多数文档中都出现,其IDF值很低,导致最终的TFIDF值也很低。相反,词 `b` 尽管在当前文档中出现次数非常多(1000次),但由于它只在少数文档中出现,其IDF值很高,因此最终的TFIDF值也较高。
    • 这说明,TFIDF值不仅取决于词在当前文档中的出现频率,还取决于这个词在整个文档集合中的稀缺性。一个词在当前文档中出现次数多并不意味着它的TFIDF值就高,还需要考虑它在整个文档集合中的分布情况。
    • 所以,训练模型时使用的语料素材非常重要。
  • TF-IDF (词频-逆文件频率)简介。

    简介

    TF-IDF(Term Frequency-Inverse Document Frequency)是一种在信息检索和文本挖掘中广泛使用的统计方法,用于评估一个词在一个文档中的重要性。它结合了两个关键概念:词频(Term Frequency, TF)和逆文档频率(Inverse Document Frequency, IDF)。

    词频 (Term Frequency, TF)

    定义:词频是指某个词在文档中出现的次数。为了防止文档长度对结果的影响,通常会将词频进行归一化处理。

    公式:
    \[ \text{TF}(t, d) = \frac{\text{词t在文档d中出现的次数}}{\text{文档d中所有词的总数}} \]

    例如,如果词 `apple` 在文档 `d` 中出现了5次,而文档 `d` 总共有100个词,那么 `apple` 的TF值为:
    \[ \text{TF}(apple, d) = \frac{5}{100} = 0.05 \]

    逆文档频率 (Inverse Document Frequency, IDF)

    **定义**:逆文档频率衡量了一个词在整个文档集合中的普遍重要性。如果一个词在很多文档中都出现,那么它的IDF值会较低;反之,如果一个词只在少数文档中出现,那么它的IDF值会较高。

    **公式**:
    \[ \text{IDF}(t) = \log\left(\frac{\text{总文档数}}{\text{包含词t的文档数} + 1}\right) \]

    其中,加1是为了防止分母为零的情况。

    例如,假设总文档数为1000,词 `apple` 出现在100个文档中,那么 `apple` 的IDF值为:
    \[ \text{IDF}(apple) = \log\left(\frac{1000}{100 + 1}\right) = \log(9.90099) \approx 0.996 \]

    TF-IDF

    **定义**:TF-IDF是词频和逆文档频率的乘积,用来表示一个词在文档中的重要性。

    **标准公式**:
    \[ \text{TF-IDF}(t, d) = \text{TF}(t, d) \times \text{IDF}(t) \]

    公式推导

    综合上述两个部分,TF-IDF的完整公式如下:

    \[ \text{TF-IDF}(t, d) = \left( \frac{\text{词t在文档d中出现的次数}}{\text{文档d中所有词的总数}} \right) \times \log\left(\frac{\text{总文档数}}{\text{包含词t的文档数} + 1}\right) \]

     示例

    假设我们有一个文档集合,总共有1000个文档。考虑以下情况:

    • 文档 `d` 中有100个词。
    • 词 `apple` 在文档 `d` 中出现了5次。
    • 词 `apple` 出现在100个文档中。

    计算 `apple` 在文档 `d` 中的TF-IDF值:

    • 计算TF
      \[ \text{TF}(apple, d) = \frac{5}{100} = 0.05 \]
    • 计算IDF
      \[ \text{IDF}(apple) = \log\left(\frac{1000}{100 + 1}\right) = \log(9.90099) \approx 0.996 \]
    • 计算TF-IDF
      \[ \text{TF-IDF}(apple, d) = 0.05 \times 0.996 \approx 0.0498 \]

    变体和优化

    虽然上面介绍的是标准的TF-IDF公式,但在实际应用中,可能会有一些变体和优化:

    • 平滑处理: 为了防止IDF值过高,有时会在IDF公式中添加一个小常数 \( k \):
      \[ \text{IDF}(t) = \log\left(\frac{\text{总文档数}}{\text{包含词t的文档数} + k}\right) \]
      常见的 \( k \) 值是1。
    • 不同的对数底数: 有些实现使用自然对数(以 \( e \) 为底),而有些使用以10或2为底的对数。选择不同的底数会影响IDF值的具体数值,但不会改变其相对大小。
    • 词频归一化: 除了简单的频率外,还可以使用其他方式来归一化词频,例如平方根归一化:
      \[ \text{TF}(t, d) = \sqrt{\frac{\text{词t在文档d中出现的次数}}{\text{文档d中所有词的总数}}} \]

    应用

    TF-IDF广泛应用于各种文本分析任务,包括但不限于:

    **信息检索**:提高搜索结果的相关性。
    **文本分类**:识别文档的主题或类别。
    **关键词提取**:找出文档中的重要词汇。
    **文档相似度计算**:比较不同文档之间的相似度。

     

  • flux模型Flux.1 Schnell和Flux. 1 Dev 有什么区别?怎么选择?

    在 AI 成像领域,Flux.1 凭借其创建高分辨率和真实图像的能力,取得了一个有趣的开端。在本文中,我们将总结 Flux.1 Schnell 和 Flux.1 Dev 之间的区别,这被认为是非常适合普通用户并受到广泛关注的两个版本。

     

    Flux.1 Schnell

    对于 Flux.1 Schnell,“Schnell”是“fast”的德语翻译,这意味着此版本专为希望获得快速一致结果的用户而设计,强调更高的性能和处理速度。

     

    • 处理速度
      • Flux.1 Schnell 专注于减少创建图像所花费的时间。非常适合需要快速见效的任务。
    • 资源管理
      • 由于它注重速度,该模型具有高效的资源管理,从而降低了功耗和处理时间。
    • 质量下降
      • 对于更高的速度,有时图像的细节或复杂性可能会有所减少,但对于那些想要快速图像而不需要最大分辨率的人来说,这可以被认为是一种有价值的交换。

     

    Flux.1 Dev

    Flux.1 Dev “Dev”代表“Developer”,意思是开发人员,它被认为是为那些想要深入使用 AI 模型的人设计的,例如开发人员、研究人员或需要根据要求定制模型以达到特定要求的人。

     

    • 开发特点
      • Flux.1 Dev 附带特定于开发人员的工具和功能,这些工具和功能提供对内部参数配置、模型测试和深入结果分析的访问。
    • 灵活性
      • 此模型旨在允许用户自定义 AI 功能以满足其特定需求。它在测试和开发方面具有高度的灵活性,非常适合那些想要研究或开发新功能的人。
    • 具体用途
      • Flux.1 Dev 是那些想要以自己的方式创建和自定义模型的人的最佳工具,这使得创建最高质量的图像并满足特定要求成为可能。

     

    如何选择?

    • 尽管 Flux.1 Schnell 和 Flux.1 Dev 是相同的基本工具,每个版本的设计和用途使它们适用于不同的应用程序。
    • 如果您是想要快速创建图像并且不想复杂配置 Flux.1 的普通用户,那么理论上 Schnell 是最合适的选择。
    • 但是,如果您是需要深入使用 AI 并需要高度灵活的工具的研究人员或开发人员,那么 Flux.1 Dev 将是进一步研究和开发的响应速度最快的选择。

    实际情况下,Flux.1 Dev 几乎总是更好的选择。Flux.1 Schnell的唯一作用可能是让人关注这个细节。

     

     

    官方下载链接

    https://huggingface.co/black-forest-labs

     

     

     

  • 本地部署大模型时,怎么选择合适的版本?

    显卡

    以通义千问为例。

    1. Qwen2.5-7B
      • 参数量:70亿(7B)
      • 显存需求:大约需要8-12GB VRAM(具体取决于优化技术)
    2. Qwen2.5-14B
      • 参数量:140亿(14B)
      • 显存需求:大约需要16-24GB VRAM(具体取决于优化技术)
    3. Qwen2.5-32B
      • 参数量:320亿(32B)
      • 显存需求:大约需要32-48GB VRAM(具体取决于优化技术)

     

    因为我的显卡是16GB 显存,所以Qwen2.5-7B是一个合适的选择。一般对于同一个大模型,发布者会推出很多不同参数的版本,比如0.5B,1B,3B等等,大家可以根据自己的显卡配置进行选择

    • Qwen2.5-7B:可以在16GB VRAM的显卡上运行良好,不会出现显存不足的情况。
    • Qwen2.5-14B:虽然理论上可以在16GB VRAM的显卡上运行,但可能会遇到显存不足的问题,特别是在进行复杂的推理任务时。如果使用一些显存优化技术(如混合精度训练、分批加载等),可以勉强运行,但体验可能不佳。
    • Qwen2.5-32B:这个版本显然不适合16GB VRAM的显卡,因为显存需求远超显卡容量。

     

    计算公式

    显存需求 ≈ 参数数量 × 精度字节 × 1.2(中间变量开销)

    • 参数数量 :
      • 模型的参数规模(如7B表示70亿参数)。
    • 精度字节 :
      • FP32(单精度浮点):4字节/参数
      • FP16(半精度浮点):2字节/参数
      • Int8(8-bit量化):1字节/参数
      • 4-bit量化:0.5字节/参数

     

    • 示例计算:
      • Qwen2.5-7B(FP16): 7B × 2字节 × 1.2 ≈ 14GB
      • Qwen2.5-14B(4-bit量化): 14B × 0.5字节 × 1.2 ≈ 8.4GB

    机器内存和大模型文件大小

    大模型文件最好选用gguf格式文件。

    大部分本地部署大模型的软件在使用大模型时,一般是将大模型加载到内存里,所以本地机器的内存大小也是需要考虑的,如果16G内存的电脑,仅加载模型文件就需要使用好几G,会导致机器运行卡顿。

     

     

     

  • ControlNet With SDXL Model

    怎么选择模型?

    Canny models

    Use the Canny ControlNet to copy the composition of an image.

    The Canny preprocessor detects edges in the control image. The Canny control model then conditions the denoising process to generate images with those edges.

    diffusers_xl_canny_full  (推荐, 速度比较慢, 但效果最好.)
    diffusers_xl_canny_mid
    diffusers_xl_canny_small
    kohya_controllllite_xl_canny_anime
    kohya_controllllite_xl_canny
    sai_xl_canny_128lora
    sai_xl_canny_256lora
    t2i-adapter_xl_canny
    t2i-adapter_diffusers_xl_canny

    Use diffusers_xl_canny_full if you are okay with its large size and lower speed.

    Use kohya_controllllite_xl_canny if you need a small and faster model and can accept a slight change in style.

    Use sai_xl_canny_128lora for a reasonable file size while changing the style less.

    The control weight parameter is critical to generating good images. Most models need it to be lower than 1.

     

    Depth models

    Use the ControlNet Depth model to copy the composition of an image. The usage is similar to Canny but the result is different.

    Here are the depth models we are going to study.

    • diffusers_xl_depth_full
    • diffusers_xl_depth_mid
    • diffusers_xl_depth_small
    • kohya_controllllite_xl_depth_anime
    • kohya_controllllite_xl_depth
    • sai_xl_depth_128lora
    • sai_xl_depth_256lora
    • sargezt_xl_depth
    • sargezt_xl_depth_faid_vidit
    • sargezt_xl_depth_zeed
    • t2i-adapter_diffusers_xl_depth_midas
    • t2i-adapter_diffusers_xl_depth_zoe

    A depth control model uses a depth map  to condition a Stable Diffusion model to generate an image that follows the depth information.A depth map can be extracted from an image using a preprocessor or created from scratch.

    diffusers_xl_depthsai_xl_depth, and t2i-adapter_diffusers_xl_depth models perform well despite their size differences. All are safe choices.

     

     

    Recolor models

    Use the recolor models to color an back-and-white photo.

    • sai_xl_recolor_128lora
    • sai_xl_recolor_256lora

    Both the 128 and 256 Recolor Control-Lora work well.

    Use the recolor_luminance preprocessor because it produces a brighter image matching human perception.

    Be careful in crafting the prompt and the negative prompt. It can have a big effect on recoloring. Use these SDXL style prompts as your starting point.

    You don’t need to use a refiner.

     

     

    Blur models

    Use the Blur model to recover a blurry image.

    • kohya_controllllite_xl_blur_anime
    • kohya_controllllite_xl_blur

    Alternatively, you can use blur_gaussian preprocessor to blur a clear image for testing.

    Of course, some image details are lost in the blur, so you should not expect to recover the same image.

     

     

    IP-adapter

    The Image Prompt Adapter (IP-adapter) lets you use an image prompt like MidJourney. Let’s use the original example from the ControlNet extension to illustrate what it does.

     

     

    OpenPose models

    The OpenPose ControlNet model is for copying a human pose but the outfit, background and anything else.

    Here are the OpenPose models available.

    • kohya_controllllite_xl_openpose_anime
    • kohya_controllllite_xl_openpose_anime_v2
    • t2i-adapter_xl_openpose
    • t2i-adapter_diffusers_xl_openpose
    • thibaud_xl_openpose
    • thibaud_xl_openpose_256lora

     

    [Major Update] sd-webui-controlnet 1.1.400 – Official writeup of SDXL ControlNet models for WebUI.

    Stabilityai/control-lora – An overview of Stability AI’s Control LoRA models.

    kohya-ss/controlnet-lllite – Model Card of ControlNet-LLLite.

    tencent-ailab/IP-Adapter – GitHub page of the Image Prompt adapter.

  • Stable Diffusion WebUI 中采样方法(Sampling methods)详解

    在 Stable Diffusion WebUI 中,这些采样方法(Sampling methods)代表了不同的算法和技术,用于在稳定扩散模型中进行采样。下面是对这些采样方法的简要说明:

    1. Euler:使用欧拉方法进行采样,它是一种简单的数值积分方法,适用于简单的扩散模型,但可能不够准确。

    2. LMS:代表最小均方(Least Mean Square)方法,它是一种迭代算法,通过根据观测误差来调整模型参数,以逐步提高采样准确性。

    3. Heun:使用Heun方法进行采样,也称为改进的欧拉方法,它在欧拉方法的基础上进行了改进,提供了更准确的数值积分结果。

    4. DPM2:代表动态粒子扩散模型(Dynamic Particle Model 2),是一种基于粒子的采样方法,通过在扩散模型中移动粒子来进行采样。

    5. DPM++2S a:DPM++2S a是DPM++2S方法的改进版本,它可能在采样效果上有所改善。

    6. DPM++2M:DPM++2M是DPM++2S方法的另一个改进版本,它可能在采样效果上有所改善。

    7. DPM++SDE:DPM++SDE代表DPM++稳定扩散估计器,采用稳定性差分方程(Stabilized Differential Equation)方法进行采样。

    8. DPM++2M SDE:DPM++2M SDE是DPM++2M方法与稳定性差分方程(Stabilized Differential Equation)方法的结合,可能提供更准确的采样结果。

    9. DPM fast:DPM fast是DPM方法的一种快速版本,它在时间效率上进行了优化,可能会比其他方法更快,但可能损失一些采样准确性。

    10. DPM adaptive:DPM adaptive是DPM方法的一种自适应版本,它根据模型的情况自动调整采样策略,以提供更准确的采样结果。

    11. LMS Karras:LMS Karras是LMS方法的Karras改进版本,可能在采样效果上有所改善。

    12. DPM2 Karras:DPM2 Karras是DPM2方法的Karras改进版本,可能在采样效果上有所改善。

    13. DPM2 a Karras:DPM2 a Karras是DPM2 a方法的Karras改进版本,可能在采样效果上有所改善。

    14. DPM++2S a Karras:DPM++2S a Karras是DPM++2S a方法的Karras改进版本,可能在采样效果上有所改善。

    15. DPM++2M Karras:DPM++2M Karras是DPM++2M方法的Karras改进版本,可能在采样效果上有所改善。

    16. DPM++SDE Karras:DPM++SDE Karras是DPM++SDE方法的Karras改进版本,可能在采样效果上有所改善。

    17. DPM++2M SDE Karras:DPM++2M SDE Karras是DPM++2M SDE方法的Karras改进版本,可能在采样效果上有所改善。

    18. DDIM:代表可微分扩散不变量采样方法(Differentiable Diffusion Invariant Moment),是一种基于扩散不变量的采样方法,可以提供更精确的采样结果。

    19. PLMS:代表部分最小均方(Partial Least Mean Square)方法,它是一种迭代算法,类似于LMS方法,但在计算上更高效。

    20. UniPC:代表统一粒子扩散模型(Unified Particle Model),是一种基于粒子的采样方法,适用于多种扩散模型。

    要选择合适的采样方法,需要考虑模型的复杂性、准确性要求和计算资源等因素。通常,更复杂的模型可能需要更精确的采样方法,而对于简单模型,一些快速的方法可能已经足够。此外,还可以根据对采样结果的评估和比较来选择最适合的方法。

  • Python Scrapy 爬虫框架

    Scrapy 相关细节

    • 安装scrapy框架
      • pip install scrapy -i https://pypi.douban.com/simple
    • 创建scrapy爬虫
      • 进入项目文件夹
        • cd project_name/project_name/spiders
      • 运行命令
        • scrapy startproject project_name url
        • D:\Practice\Python\Scrapy_20230226\scrapy_carhome\scrapy_carhome\spiders> scrapy genspider car https://car.autohome.com.cn/price/brand-15.html
    • 运行爬虫
      • 进入当前爬虫所在文件夹
        • scrapy crawl spider_name
        • D:\Practice\Python\Scrapy_20230226\scrapy_carhome\scrapy_carhome\spiders> scrapy crawl car

     

     

     

    Scrapy 数据流程

    https://docs.scrapy.org/en/latest/topics/architecture.html

    1. 引擎得到来自于Spider的url请求
    2. 引擎调用调度器将待爬取url放入列队中
    3. 调度器返回请求给引擎
    4. 引擎发送请求给下载器
    5. 下载器获取互联网资源, 获取数据后返回给引擎
    6. 引擎收到来自下载器的响应, 将其发送给Spider, 以进行处理
    7. Spider返回处理过的数据给引擎(同时也发送下一个新的URL给引擎)
    8. 引擎发送处理过的数据给Pipelines, 以保存数据
    9. 重复第三步之后的步骤, 直到没有新的任务

     

     

    The data flow in Scrapy is controlled by the execution engine, and goes like this:

    1. The Engine gets the initial Requests to crawl from the Spider.
    2. The Engine schedules the Requests in the Scheduler and asks for the next Requests to crawl.
    3. The Scheduler returns the next Requests to the Engine.
    4. The Engine sends the Requests to the Downloader, passing through the Downloader Middlewares (see process_request()).
    5. Once the page finishes downloading the Downloader generates a Response (with that page) and sends it to the Engine, passing through the Downloader Middlewares (see process_response()).
    6. The Engine receives the Response from the Downloader and sends it to the Spider for processing, passing through the Spider Middleware (see process_spider_input()).
    7. The Spider processes the Response and returns scraped items and new Requests (to follow) to the Engine, passing through the Spider Middleware (see process_spider_output()).
    8. The Engine sends processed items to Item Pipelines, then send processed Requests to the Scheduler and asks for possible next Requests to crawl.
    9. The process repeats (from step 3) until there are no more requests from the Scheduler.

     

     

  • Python 环境问题

    Pycharm

    Anaconda

    Conda

    创建Conda虚拟环境

    • 指定python版本和环境名称
      • conda create -n env_name python=3.8
    • 指定python版本, 环境名称, 环境安装路径
      • conda create –prefix=C:/ProgramData/Anaconda3/envs/ENV_spider python=3.9
    • 激活所安装的环境
      • conda activate env_name
    • 在当前激活的环境里安装包
      • conda install package_name
    • 退出当前激活的环境
      • conda deactivate

    PIP

     

    Jupyter

    安装内核

    • 激活/切换到要添加的虚拟环境, 确认是否安装 ipykernel
      • python -m ipykernel –version
    • 如果没有,则安装
      • python -m pip install ipykernel
    • 为 jupyter 添加内核
      • python -m ipykernel install –name env_name

    查看已安装内核

    • jupyter kernelspec list
    (ENV_machinelearning) λ jupyter kernelspec list
    Available kernels:
      %s    %s python3             C:\ProgramData\Anaconda3\envs\ENV_machinelearning\share\jupyter\kernels\python3
      %s    %s pycharm-78712231    C:\Users\Jin\AppData\Roaming\jupyter\kernels\pycharm-78712231
      %s    %s pycharm-ae39b4c1    C:\Users\Jin\AppData\Roaming\jupyter\kernels\pycharm-ae39b4c1
      %s    %s env_machinelearning C:\ProgramData\jupyter\kernels\env_machinelearning

     

    删除 jupyter 内核

    • jupyter kernelspec remove kernelname

     

     

     

  • Django Rest Framework

    Web应用开发模式

    前后端分离

     

    通俗地讲, 其实判定很简单:如果前端和后端这两个角色, 只通过API 文档就能进行数据交流,就说明他们的逻辑是分离的。我们可以称之为 “前后端代码分离”。

    如果除了 API 文档之外还需要各种其他的数据交流方式,比如后端把数据藏在一个 div 的属性里,那么就不是前后端分离的。像各种框架里的模板引擎和渲染功能。

    至于所谓的 “最佳实践”并不存在, 但通用的, 类似的方案如下:

    • 前端服务器
      • 将前端的所有代码使用javascript+node.js的方式部署在node.js服务器上,作为前端稳定的服务,提供页面使用。
    • 后端服务器
      • 后端的服务器写各种业务逻辑代码, 如业务逻辑, 鉴权逻辑.
    • 前后端交互
      • 后端服务为前端提供一个统一的访问入口(一般是单独部署一个服务),使用restful等风格提供http服务供前端调用,实现前后端数据传输(通常以JSON来交换数据)。

     

     

    这样一来, 现在 前端服务器不再处理任何业务,它接收到请求后,经过转换,发送给各个相关后端服务器,将各个后端服务器返回的,处理过的业务数据填入 HTML 模板,最后发送给浏览器。前端服务器和后端服务器间,可以选用任何你觉得合适的通信手段,可以是 REST,可以是 RPC,选用什么样的通信手段,这是另一个议题了。

    前后端分离, 是浏览器和后端服务分离吗?

    不是,前后端分离里的前端不是浏览器,指的是生成 HTML 的那个服务,它可以是一个仅仅生成 HTML 的 Web 服务器,也可以是在浏览器中通过 JS 动态生成 HTML 的 单页应用。

     

    实践中,有实力的团队往往在实现前后端分离里时,前端选用 node 服务器,后端选用 C#、Java 等。

    API接口规范

     

    Restful

    • 基于http协议
    • 传统的路由设计
      • 增: localhost.com/user/add
      • 删: localhost.com/user/delete/1
      • 改: localhost.com/user/edit/1
      • 查: localhost.com/user/query/1
    • Restful的设计规范, 基于资源, 资源操作由请求方式决定
    • 比如 api.localhost/users/
      • 增: post
      • 删: delete
        • 删除单个资源: api.localhost.com/users/1
        • 删除所有资源: localhost/users/
      • 改:
        • put: 更新单个资源
        • patch: 更新局部/部分资源
      • 查: get
        • 查单个资源: api.localhost.com/users/1
        • 查所有资源: api.localhost.com/users/
      • 过滤,通过在url上传参的形式传递搜索条件
        • https://api.localhost.com/v1/zoos?limit=10:指定返回记录的数
        • https://api.localhost.com/v1/zoos?offset=10:指定返回记录的开始位置
        • https://api.localhost.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
        • https://api.localhost.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
        • https://api.localhost.com/v1/zoos?animal_type_id=1:指定筛选条件

    RPC

    • 性能更高
    • 基于TCP/IP协议(传输层)

    序列化

    什么是序列化?

    序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。

    • 序列化:把对象转化为可传输的字节序列过程称为序列化。
    • 反序列化:把字节序列还原为对象的过程称为反序列化。

     

    为什么要序列化?

    如果光看定义我想你很难一下子理解序列化的意义,那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么?

    其实序列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组

     

    在大多数开发者, 所谓序列化是指数据在 对象和json之前的相互转换.

     

     

    Django Rest Framework

    DRF的安装

    创建新环境

    conda create –prefix=C:/ProgramData/Anaconda3/envs/ENV_DRF_01 python=3.9

    – 激活环境

    • conda activate C:\ProgramData\Anaconda3\envs\ENV_DRF_01

    安装django

    • conda install django=3.2.*

    创建django项目

    • django-admin startproject myshop

    安装drf, drf是基于django的一个包

    • pip install djangorestframework
    • pip install pymysql

    创建django项目内的APP

    • python manage.py startapp LearnDRF

    进行django配置

    • 数据库
    • INSTALLED_APPS

     

    DRF快速体验

    创建并配置数据库

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'learndrf',  # 数据库名字
            'USER': 'root',
            'PASSWORD': 'zxcvbnm',
            'HOST': '127.0.0.1',  # 那台机器安装了MySQL
            'PORT': 3306,
        }
    }

    模型类

    class Student(models.Model):
        # 模型字段
        name = models.CharField(max_length=100,verbose_name="姓名")
        sex = models.BooleanField(default=1,verbose_name="性别")
        age = models.IntegerField(verbose_name="年龄")
        class_null = models.CharField(max_length=5,verbose_name="班级编号")
        description = models.TextField(max_length=1000,verbose_name="个性签名")
    
        class Meta:
            db_table="tb_student"
            verbose_name = "学生"
            verbose_name_plural = verbose_name

    执行迁移

    python manage.py makemigrations
    python manage.py migrate

    可以添加一些数据, 方便测试.

     

    FBV 和CBV

    FBV

    fbv是指function based view, 望文生义, 义如其名.

    fbv 模式的路由

    path(‘login_fbv/’, login)

    • login是view.py的login函数.
    CBV

    path(‘student/’, StudentView.as_view())

    • StudentView是view.py的类
    • as_view()是drf包里面视图方法

     

    DRF的使用

    DRF的优势?

    谈到DRF的使用, 很容易会产生一堆问题? DRF有什么作用? 通常用于哪些场景? 它的优势是什么? 为什么用DRF, 而不是django?

    一句话回答: DRF可以更快, 更高效的完成REST API开发.

    DRF如何实现这些优势的?

    DRF包含一系列高度抽象和功能丰富的组件

    序列化组件

    1.作用: 把python中的对象,转成json格式字符串

    使用步骤1: 写一个类继承Serializer或者ModelSerializer
    举例(类中选取字段进行序列化):
    class BookSerializer(serializers.Serializer):
    id = serializers.CharField()
    title = serializers.CharField()
    price = serializers.CharField()
    举例(把类中字段全部进行序列化):

    class TestSer(serializers.ModelSerializer):
    class Meta:
    model = models.Takes
    fields = ‘__all__’
    总结:
    变量名和source指定的值不能一样

    source=’publish.name’还支持继续 .

    source 还支持方法(没用)

    支持写方法,如下
    方法一定传一个参数,是当前book对象

    publish_dic=serializers.SerializerMethodField()
    def get_publish_dic(self,obj):
    return
    结果:{‘id’:obj.publish.pk,’name’:obj.publish.name}

    认证组件

    1.认证组件的好处:
    比如要访问books/路径,必须登录之后才能访问。一旦登录成功,在响应结果中写一个随机字符串

    举例: {status:100
    msg:登录成功
    token:sdafsdfasd
    }

    2.使用步骤1:写一个类,继承BaseAuthentication

    3.使用步骤2:def authenticate(self,request) ,记住传request对象

    4.在视图类中使用:(不要加括号):

    1.局部使用:authentication_classes=[AuthLogin](写在views中)

    2.全局使用:-REST_FRAMEWORK=
    {“DEFAULT_AUTHENTICATION_CLASSES”:[“app01.auth.AuthLogin”,](在setting中配置)

    3.全局使用的局部禁用:authentication_classes = [](写在views中)

     

    权限组件

    1.使用步骤1:写一个类,继承BasePermission

    2.使用步骤2:def has_permission(self, request, view): ,记住传
    request对象和view

    3.在视图类中使用:(不要加括号):

    1.局部使用:permission_classes=[MyPer](写在views中)

    2.全局使用:-REST_FRAMEWORK={“DEFAULT_PERMISSION_CLASSES”:[‘app01.auth.MyPer’]}(在setting中配置)

    3.全局使用的局部禁用:permission_classes = [](写在views中)

     

    节流组件

    1.使用步骤1:写一个类,继承SimpleRateThrottle

    2.使用步骤2:def get_cache_key(self, request, view):,记住传request对象和view

    3.使用步骤3
    :’DEFAULT_THROTTLE_RATES’: {
    这个key跟scope对应,value值3/m 3/h 4/d
    ‘xx’: ‘3/m’
    }(在setting中配置)

    节流作用:节流(Throttling)类似于权限,因为它决定了是否应该对请求进行授权。节流表示一个临时状态,并用于控制客户端对API的请求率。

    视图组件

    使用:
    1.局部使用:throttle_classes = [VisitThrottle](写在views中)
    2.全局使用:REST_FRAMEWORK={“DEFAULT_THROTTLE_CLASSES”:[“app01.auth.VisitThrottle”]}(在setting中配置)
    3.全局使用的局部禁用:throttle_classes = [](写在views中)

    视图作用: 数据库查询, 2. 构建序列化器, 进行序列化操作, 返回数据

     

    解析器

    1.局部使用:parser_classes=[JSONParser,](写在views中)
    2.全局使用:’DEFAULT_PARSER_CLASSES’:[‘rest_framework.parsers.JSONParser’](在setting中配置)

    解析器作用:解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。

     

    渲染器

    DRF提供的渲染器有很多,默认是
    ‘DEFAULT_RENDERER_CLASSES’: (
    ‘rest_framework.renderers.JSONRenderer’,
    ‘rest_framework.renderers.BrowsableAPIRenderer’,
    ),

    渲染器的作用:渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。

     

    分页组件

    1.路由

    urlpatterns = [
    re_path(‘(?P<version>[v1|v2]+)/page1/’, Pager1View.as_view(),) #分页1
    ]
    序列化

    from rest_framework import serializers
    from api import models
    class PagerSerialiser(serializers.ModelSerializer):
    class Meta:
    model = models.Role
    fields = “__all__”
    视图

    from api.utils.serializsers.pager import PagerSerialiser
    from rest_framework.response import Response
    from rest_framework.pagination import PageNumberPagination
    class Pager1View(APIView):
    def get(self,request,*args,**kwargs):
    #获取所有数据
    roles = models.Role.objects.all()
    #创建分页对象
    pg = PageNumberPagination()
    #获取分页的数据
    page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
    #对数据进行序列化
    ser = PagerSerialiser(instance=page_roles,many=True)
    return Response(ser.data)
    settings配置

    REST_FRAMEWORK = {
    #分页
    “PAGE_SIZE”:2 #每页显示多少个
    }

    版本组件

    自定义版本处理

    class ParamVersion(object):
    def determine_version(self, request, *args, **kwargs):
    # version = request._request.GET.get(“version”)
    # version = request.query_params.get(‘version’)
    version = request.GET.get(“version”)
    return version

    class UsersView(APIView):
    versioning_class = ParamVersion

    def get(self, request, *args, **kwargs):
    version = request.version
    return JsonResponse({‘version’: version})

    路由组件

    使用方法

    # format参数是给渲染器用的
    url(r’^(?P<version>[v1|v2]+)/myview\.(?P<format>\w+)$’, MyView.as_view({“get”: “list”, “post”: “create”})),
    创建router对象,并注册视图集。

    from rest_framework import routers
    router = routers.DefaultRouter()
    router.register(r’myview’, MyView)

    最原始的 Serializer + APIView

    2. ModelSerializer + APIView

     

    序列化和视图类的使用

    前置了解

    视图类

    • APIView
      • 继承APIView父类(Django中View的子类)
      • 具备View的所有特性
      • 提供了认证、授权、限流等功能
    • GenericAPIView
      • GenericAPIView为APIView的子类,它拓展了过滤、查询、分页的功能

    序列化器

    • Serializer
    • ModelSerializer

    这里将会通过以下三种组合来熟悉DRF的写法.

    DRF的 Serializer + DRF的 APIView

    DRF的 ModelSerializer + DRF的 APIView

    DRF的 ModelSerializer + DRF的 GenericAPIView

     

    Serializer + APIView

    path('student/', StudentView.as_view()), #drf的as_view方法
    re_path('student/(\d+)/', StudentDetailView.as_view()), #drf的as_view方法

     

    class StudentView(APIView):
    
    
        def get(self, request):
            print(request.GET)
            print(request.query_params)
            students = Student.objects.all()
            print(type(students))
            # students 是一个queryset 数据集. 所谓的序列化就是对该数据再进行封装, 这里其实是for循环, 将里面的数据都拿到,
    
            serializer = StudentsSerializer(instance=students, many=True)
            return Response(serializer.data)
    
    
    
        def post(self, request):
            print(request)
            print(request.data)
            print(type(request.data))
    
            serializer = StudentsSerializer(data=request.data)
            if serializer.is_valid():
                stu = Student.objects.create(**serializer.validated_data)
                ser = StudentsSerializer(instance=stu, many=False)
                return Response(ser.data)
    
            else:
                 return Response(serializer.errors)
    
    
    
    class StudentDetailView(APIView):
    
        print('StudentDetailView')
        def get(self, request, id):
            students = Student.objects.get(pk=id)
            serializer = StudentsSerializer(instance=students, many=False)
            return Response(serializer.data)
    
    
        def delete(self, request, id):
            Student.objects.get(pk=id).delete()
    
            return Response("已删除")
    
        def put(self, request, id):
            print(request)
            print(request.data)
            print(type(request.data))
    
            serializer = StudentsSerializer(data=request.data)
            if serializer.is_valid():
                print(serializer.validated_data)
    
                # 成功修改数据的条数, 为1
                # n = Student.objects.filter(pk=id).update(**serializer.validated_data)
                print(n)
    
                #要修改的数据对象
                stu = Student.objects.get(id=id)
                print(id)
                ser = StudentsSerializer(instance=stu, many=False)
                print(ser.data)
                return Response(ser.data)
    
            else:
                return Response(serializer.errors)

    get 查询所有数据

    • http://127.0.0.1:8000/student/

    post 添加数据

    • http://127.0.0.1:8000/student/

    delete 删除单条数据

    • http://127.0.0.1:8000/student/1/

    put 修改单条数据

    • http://127.0.0.1:8000/student/1/

    get 查询单条数据

    • http://127.0.0.1:8000/student/1/

    ModelSerializer + APIView

    path('publish/', PublishView.as_view()),
    re_path('publish/(\d+)/', PublishDetailView.as_view()),

     

    class PublishSerializer(serializers.ModelSerializer):
        class Meta:
            model = Publish
            # fiedls = "__all__"
            fields = ["name", "email"]
    
    
    class PublishView(APIView):
        def get(self, reqeust):
            publishes = Publish.objects.all()
            ps = PublishSerializer(instance=publishes, many=True)
            return Response(ps.data)
    
        def post(self, request):
            print(request.data, type(request.data))
    
            serializer = PublishSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()  # create方法 将符合条件的数据插入到数据库
                return Response(serializer.data)
            else:
                return Response(serializer.errors)
    
    
    class PublishDetailView(APIView):
    
        def get(self, reqeust, id):
            publish = Publish.objects.get(pk=id)
            ps = PublishSerializer(instance=publish, many=False)
            return Response(ps.data)
    
        def delete(self, request, id):
            Publish.objects.get(pk=id).delete()
            return Response()
    
        def put(self, request, id):
            # post请求
            instance = Publish.objects.get(pk=id)
            serializer = PublishSerializer(data=request.data, instance=instance)
    
            if serializer.is_valid():
                serializer.save()  # instance的 update
                return Response(serializer.data)
    
            else:
                return Response(serializer.errors)

     

     

    ModelSerializer + GenericAPIView

    path('author/', AuthorView.as_view()),
    re_path('author/(?P<pk>\d+)/', AuthorDetailView.as_view()),

     

    from rest_framework.generics import GenericAPIView
    
    
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = ["name", "age"]
    
    
    class AuthorView(GenericAPIView):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
        def get(self, reqeust):
            # serializer = self.serializer_class(instance=self.queryset,many=True)
            serializer = self.get_serializer(instance=self.get_queryset(), many=True)
    
            return Response(serializer.data)
    
        def post(self, request):
    
            serializer = self.get_serializer(data=request.data)
    
            if serializer.is_valid():
                serializer.save()  # create方法 将符合条件的数据插入到数据库
                return Response(serializer.data)
            else:
    
                return Response(serializer.errors)
    
    
    class AuthorDetailView(GenericAPIView):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
        def get(self, request, pk):  # url的pk名
            serializer = self.get_serializer(instance=self.get_object(), many=False)
            return Response(serializer.data)
    
        def delete(self, request, pk):
            self.get_object().delete()
            return Response()
    
        def put(self, request, id):
            # post请求
            instance = self.get_object()
            serializer = self.get_serializer(data=request.data, instance=instance)
    
            if serializer.is_valid():
                serializer.save()  # instance的 update
                return Response(serializer.data)
    
            else:
                return Response(serializer.errors)

     

    ModelSerializer + Mixin

    from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, \
        UpdateModelMixin
    
    from rest_framework.generics import GenericAPIView
    
    
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = ["name", "age"]
    
    
    class AuthorView(GenericAPIView, ListModelMixin, CreateModelMixin):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
        def get(self, reqeust):
            return self.list(reqeust)
    
        def post(self, request):
            return self.create(request)
    
    
    class AuthorDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
        def get(self, request, pk):  # url的pk名
            return self.retrieve(request)
    
        def delete(self, request, pk):
            return self.destroy(request)
    
        def put(self, request, pk):
            return self.update(request)

    基于Mixin的进一步封装

    ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, \ UpdateModelMixin

     

    from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
    
    
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = ["id","name", "age"]
    
    
    class AuthorView(ListCreateAPIView):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
    
    class AuthorDetailView(RetrieveUpdateDestroyAPIView):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer