LLM之RAG实战(三十九)| 高级RAG技术全面解析(附代码)

一、高级RAG概述

       基本 RAG 的工作流程可分为三个步骤:索引检索生成。在索引阶段,文本被转换为嵌入,然后存储在向量数据库中以创建可搜索的索引。在检索步骤中,用户的查询也被转换为嵌入,此嵌入用于在向量数据库中搜索最相关的文本数据。最后,在生成步骤中,查询会使用先前检索到的相关文档进行增强,大型语言模型会使用此增强的提示来生成对用户问题的答案。

       高级 RAG是在基本RAG流程基础上添加了很多新步骤(子步骤)。以下是本文将讨论的增强点列表,但总体列表并不仅限于这些。

  • Data Indexing Optimizations(数据索引优化):使用滑动窗口进行文本分块和有效利用元数据等技术来创建更易于搜索和更有条理的索引。
  • Query Enhancement(查询增强):使用同义词或更广泛的术语修改或扩展初始用户查询,以改进相关文档的检索。
  • Hybrid Search(混合搜索):将传统的基于关键字的搜索与使用嵌入向量的语义搜索相结合,以处理各种查询复杂性。
  • Fine Tuning Embedding Model(微调嵌入模型):调整预先训练的模型以更好地理解特定领域的细微差别,提高检索到的文档的准确性和相关性。
  • Response Summarization(响应摘要):在最终生成响应之前,浓缩检索到的文本以提供简洁且相关的摘要。
  • Re-ranking and Filtering(重新排序和过滤):根据相关性调整检索到的文档的顺序,并过滤掉不太相关的结果以优化最终输出。

      关于RAG的更多高级优化技术,可以参考论文《A Survey on Retrieval-Augmented Text Generation for Large Language Models》[1]

二、高级RAG之预检索

       预检索是定义 a) 如何进行索引以及 b) 在将用户查询用于检索之前对其进行哪些操作的步骤。下面,我将讨论预检索优化的各种策略,包括数据索引和查询增强,并提供示例 Python 代码示例。

2.1 数据索引优

       在做其他事情之前,我们必须先存储数据,以便以后查询,这称为索引。这包括设置正确的块大小、有效使用元数据以及选择嵌入模型。

2.1.1. 用于文本分块的滑动窗口

        索引文本的一种简单方法是将文本拆分为 n 个部分,将它们转换为嵌入向量,然后将它们存储在向量数据库中。滑动窗口方法创建重叠的文本块,以确保在块的边界处不会丢失任何上下文信息。以下代码示例使用 nltk 库按句子拆分文本。

import nltk
from nltk.tokenize import sent_tokenize
​
nltk.download('punkt')  # Ensure the punkt tokenizer is downloaded
​
def sliding_window(text, window_size=3):
    """
    Generate text chunks using a sliding window approach.
​
    Args:
    text (str): The input text to chunk.
    window_size (int): The number of sentences per chunk.
​
    Returns:
    list of str: A list of text chunks.
    """
    sentences = sent_tokenize(text)
    return [' '.join(sentences[i:i+window_size]) for i in range(len(sentences) - window_size + 1)]
​
# Example usage
text = "This is the first sentence. Here comes the second sentence. And here is the third one. Finally, the fourth sentence."
chunks = sliding_window(text, window_size=3)
for chunk in chunks:
    print(chunk)
    print("-----")
    # here, you can convert the chunk to embedding vector
    # and, save it to a vector database

2.1.2. 元数据利用

       元数据可以包含文档创建日期、作者或相关标签等信息,这些信息可用于在检索过程中筛选或确定文档的优先顺序,从而增强搜索过程。

       以下代码示例:使用 faiss 库创建一个向量数据库,并将向量插入其中并通过元数据(标签)进行搜索。

import numpy as np
import faiss
​
documents = [
    "Document 1 content here",
    "Content of the second document",
    "The third one has different content",
]
metadata = [
    {"date": "20230101", "tag": "news"},
    {"date": "20230102", "tag": "update"},
    {"date": "20230103", "tag": "report"},
]
​
# Dummy function to generate embeddings
def generate_embeddings(texts):
    """Generate dummy embeddings for the sake of example."""
    return np.random.rand(len(texts), 128).astype('float32')  # 128-dimensional embeddings
​
# Generate embeddings for documents
doc_embeddings = generate_embeddings(documents)
​
# Create a FAISS index for the embeddings (using FlatL2 for simplicity)
index = faiss.IndexFlatL2(128)  # 128 is the dimensionality of the vectors
index.add(doc_embeddings)  # Add embeddings to the index
​
# Example search function that uses metadata
def search(query_embedding, metadata_key, metadata_value):
    """Search the index for documents that match metadata criteria."""
    k = 2  # Number of nearest neighbors to find
    distances, indices = index.search(np.array([query_embedding]), k)  # Perform the search
    results = []
    for idx in indices[0]:
        if metadata[idx][metadata_key] == metadata_value:
            results.append((documents[idx], metadata[idx]))
    return results
​
# Generate a query embedding (in a real scenario, this would come from a similar process)
query_embedding = generate_embeddings(["Query content here"])[0]
​
# Search for documents tagged with 'update'
matching_documents = search(query_embedding, 'tag', 'update')
print(matching_documents)

2.2 查询增强

       在某些情况下,用户无法清楚地表达问题。在这种情况下,我们可以通过完全重写或扩展查询来增强查询。

       我们可以利用 LLM 本身。我们可以将问题发送给 LLM,并要求其更好地表达。以下提示将有助于此。

Given the prompt: '{prompt}', generate 3 question that are better articulated.

       一旦我们有了新的查询,我们就可以将新的查询转换为嵌入向量,并使用它们在向量数据库中进行搜索。

三、高级RAG之检索技术

       检索是使用查询来搜索先前索引的数据库的步骤。下面,我将讨论各种检索策略。

3.1 混合搜索模型

       到目前为止,我们一直在讨论在向量数据库中搜索查询——我们在其中存储嵌入向量。让我们更进一步,将其与传统的基于关键字的搜索相结合。这种方法确保检索系统可以处理各种查询类型;从需要精确关键字匹配的查询到需要理解上下文的更复杂的查询。

       让我们建立一个混合搜索模型。我们将使用 Elasticsearch 作为传统搜索机制,并使用 faiss 作为向量数据库进行语义搜索。

3.1.1. 创建Elasticsearch索引

       首先假设所有文档都在“documents”字典中,并且我们已经获取了嵌入向量并将它们存储在字典中。以下代码块连接到 Elasticsearch 8.13.4 并为给定的示例文档创建索引。

ES_NODES = "http://localhost:9200"
​
documents = [
    {"id": 1, "text": "How to start with Python programming.", "vector": [0.1, 0.2, 0.3]},
    {"id": 2, "text": "Advanced Python programming tips.", "vector": [0.1, 0.3, 0.4]},
    # More documents...
]
​
from elasticsearch import Elasticsearch
​
es = Elasticsearch(
    hosts=ES_NODES,
)
for doc in documents:
    es.index(index="documents", id=doc['id'], document={"text": doc['text']})

3.1.2. 创建Faiss索引

       在这一部分中,我们使用 faiss 作为向量数据库并对向量进行索引。

import numpy as np
import faiss
​
dimension = 3  # Assuming 3D vectors for simplicity
faiss_index = faiss.IndexFlatL2(dimension)
vectors = np.array([doc['vector'] for doc in documents])
faiss_index.add(vectors)

3.1.3. 混合索引

       下面代码将Elasticsearch关键词搜索和faiss向量语义匹配进行混合搜索。

def hybrid_search(query_text, query_vector, alpha=0.5):
    # Perform a keyword search using Elasticsearch on the "documents" index, matching the provided query_text.
    response = es.search(index="documents", query={"match": {"text": query_text}})
    # Extract the document IDs and their corresponding scores from the Elasticsearch response.
    keyword_results = {hit['_id']: hit['_score'] for hit in response['hits']['hits']}
​
    # Prepare the query vector for vector search: reshape and cast to float32 for compatibility with Faiss.
    query_vector = np.array(query_vector).reshape(1, -1).astype('float32')
    # Perform a vector search with Faiss, retrieving indices of the top 5 closest documents.
    _, indices = faiss_index.search(query_vector, 5)
    # Create a dictionary of vector results with scores inversely proportional to their rank (higher rank, higher score).
    vector_results = {str(documents[idx]['id']): 1/(rank+1) for rank, idx in enumerate(indices[0])}
​
    # Initialize a dictionary to hold combined scores from keyword and vector search results.
    combined_scores = {}
    # Iterate over the union of document IDs from both keyword and vector results.
    for doc_id in set(keyword_results.keys()).union(vector_results.keys()):
        # Calculate combined score for each document using the alpha parameter to balance the influence of both search results.
        combined_scores[doc_id] = alpha * keyword_results.get(doc_id, 0) + (1 - alpha) * vector_results.get(doc_id, 0)
​
    # Return the dictionary containing combined scores for all relevant documents.
    return combined_scores
​
# Example usage
query_text = "Python programming"
query_vector = [0.1, 0.25, 0.35]
# Execute the hybrid search function with the specified query text and vector.
results = hybrid_search(query_text, query_vector)
# Print the results of the hybrid search to see the combined scores of documents.
print(results)

       该hybrid_search 函数首先使用 Elasticsearch 进行关键字搜索。下一步,它使用 Faiss 执行向量搜索,Faiss 返回前五个最接近的文档的索引,这些索引用于根据文档的排名创建反向分数文档(即,最接近的文档得分最高)。

       一旦我们获得了 Elasticsearch 和 Faiss 的结果,我们就可以把这两种方法的得分结合起来。每个文档的最终得分是使用参数 alpha加权平均值计算得到,如果alpha=0.5,意味这两个结果赋予了相同的权重。

完整的代码,可以参考[2]

3.2 微调嵌入模型

       微调嵌入模型是增强检索增强生成系统性能的有效步骤。微调预训练模型有助于模型理解特定领域或数据集的细微差别,从而可以显著提高检索到的文档的相关性和准确性。

      我们可以用以下几个要点来总结微调模型的重要性:

  • 增强语义理解:微调有助于模型掌握原始训练数据中可能无法很好体现的特定领域的术语和概念。
  • 适应内容的更新:某些领域(例如医学或技术领域)的信息正在迅速变化,通过微调保持嵌入更新可以保持系统的有效性。
  • 提高检索精度:通过使嵌入空间与目标用例更紧密地对齐,微调可确保更可靠地检索语义相关的文本。

3.2.1 准备微调数据

       以下代码块是微调模型的第一步。它初始化用于微调预训练屏蔽语言模型的管道,加载模型和标记器,并调整设备兼容性(GPU 或 CPU)。

       初始化后,它会通过标记化和动态标记掩码处理样本数据集。此设置可让模型为自监督学习做好准备,在自监督学习中,它会预测掩码标记,从而增强其对输入数据的语义理解。

# Define the model name using a pre-trained model from the Sentence Transformers library
model_name = "sentence-transformers/all-MiniLM-L6-v2"
​
# Load the tokenizer for the specified model from Hugging Face's transformers library
tokenizer = AutoTokenizer.from_pretrained(model_name)
​
# Load the model for masked language modeling based on the specified model
model = AutoModelForMaskedLM.from_pretrained(model_name)
​
# Determine if a GPU is available and set the device accordingly; use CPU if GPU is not available
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
​
# Move the model to the appropriate device (GPU or CPU)
model.to(device)
​
# Define a generator function to create a dataset; this should be replaced with actual data loading logic
def dataset_generator():
    # Example dataset composed of individual sentences; replace with your actual dataset sentences
    dataset = ["sentence1", "sentence2", "sentence3"]
    # Yield each sentence as a dictionary with the key 'text'
    for sentence in dataset:
        yield {"text": sentence}
​
# Create a dataset object using Hugging Face's Dataset class from the generator function
dataset = Dataset.from_generator(dataset_generator)
​
# Define a function to tokenize the text data
def tokenize_function(example):
    # Tokenize the input text and truncate it to the maximum length the model can handle
    return tokenizer(example["text"], truncation=True)
​
# Apply the tokenization function to all items in the dataset, batch processing them for efficiency
tokenized_datasets = dataset.map(tokenize_function, batched=True)
​
# Initialize a data collator for masked language modeling which randomly masks tokens
# This is used for training the model in a self-supervised manner
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=True, mlm_probability=0.15)

3.2.2 开始微调模型

       一旦数据准备好了,我们就可以开始微调阶段。在这个阶段,我们将使用模型的现有权重并开始更新它们。

      以下代码块使用 Hugging Face 的 API 设置并执行语言模型的训练Trainer。它首先定义训练参数(时期、批量大小和学习率等)。Trainer然后,对象使用这些设置以及预加载的模型、标记化数据集和用于屏蔽语言建模的数据整理器(模型、标记化数据集和数据整理器是在上一步中创建的)。训练完成后,将保存新的更新模型及其标记器以供下一步使用。

# Define training arguments to configure the training session
training_args = TrainingArguments(
    output_dir="output",  # Directory where the outputs (like checkpoints) will be saved
    num_train_epochs=3,  # Total number of training epochs to perform
    per_device_train_batch_size=16,  # Batch size per device during training
    learning_rate=2e-5,  # Learning rate for the optimizer
)
​
# Initialize the Trainer, which handles the training loop and evaluation
trainer = Trainer(
    model=model,  # The model to be trained, already loaded and configured
    args=training_args,  # The training arguments defining the training setup
    train_dataset=tokenized_datasets,  # The dataset to train on, already tokenized and prepared
    data_collator=data_collator,  # The data collator that handles input formatting and masking
)
​
# Start the training process
trainer.train()
​
# Define the paths where the fine-tuned model and tokenizer will be saved
model_path = "./model"
tokenizer_path = "./tokenizer"
​
# Save the fine-tuned model to the specified path
model.save_pretrained(model_path)
​
# Save the tokenizer used in training to the specified path
tokenizer.save_pretrained(tokenizer_path)

3.2.3 使用微调后的模型

       现在是时候使用保存的模型和标记器来生成嵌入向量了。以下代码块用于此目的。

       以下代码块加载模型和标记器以生成给定句子的嵌入。首先,从保存的路径加载模型和标记器,并将其加载到 GPU 或 CPU。句子(在本文的上下文中,它们是查询)被标记化。模型在不更新其参数的情况下处理这些输入,这称为推理模式,可以使用with torch.no_grad()。我们不使用此模型来预测下一个标记;相反,我们的目标是从模型的隐藏状态中提取嵌入向量。最后一步,这些嵌入向量被移回 CPU。

# Load the tokenizer and model from saved paths, ensuring the model is allocated to the appropriate device (GPU or CPU)
tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
model = AutoModelForMaskedLM.from_pretrained(model_path).to(device)
​
# Define a function to tokenize input sentences, configuring padding and truncation to handle variable sentence lengths
def tokenize_function_embedding(example):
    return tokenizer(example["text"], padding=True, truncation=True)
​
# List of example sentences to generate embeddings for
sentences = ["This is the first sentence.", "This is the second sentence."]
​
# Create a Dataset object directly from these sentences
dataset_embedding = Dataset.from_dict({"text": sentences})
​
# Apply the tokenization function to the dataset, preparing it for embedding generation
tokenized_dataset_embedding = dataset_embedding.map(tokenize_function_embedding, batched=True, batch_size=None)
​
# Extract 'input_ids' and 'attention_mask' needed for the model to understand which parts of the input are padding and which are actual content
input_ids = tokenized_dataset_embedding["input_ids"]
attention_mask = tokenized_dataset_embedding["attention_mask"]
​
# Convert these lists into tensors and ensure they are on the correct device (GPU or CPU) for processing
input_ids = torch.tensor(input_ids).to(device)
attention_mask = torch.tensor(attention_mask).to(device)
​
# Generate embeddings using the model without updating gradients to save computational resources
with torch.no_grad():
    outputs = model(input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True)
    # Extract the last layer's hidden states as embeddings, specifically the first token (typically used in BERT-type models for representing sentence embeddings)
    embeddings = outputs.hidden_states[-1][:, 0, :]
​
# Move the embeddings from the GPU back to CPU for easy manipulation or saving
embeddings = embeddings.cpu()
​
# Print each sentence with its corresponding embedding vector
for sentence, embedding in zip(sentences, embeddings):
    print(f"Sentence: {sentence}")
    print(f"Embedding: {embedding}\n")

四、高级RAG之检索后处理

       检索到相关信息后,还需要以正确顺序喂给大模型。在接下来的 2 个小节中,我们将解释如何使用摘要和重新排序来提高 RAG 的质量。

4.1 对响应进行摘要

       如果在索引过程中在数据库中存储了大量的块文本向量,则此步骤可能是必要的。如果文本已经很小,则可能不需要此步骤。

       以下代码块可用于摘要过程。以下代码块使用该transformers库通过预先训练的 BART 模型来提取文本摘要。该函数summarize_text接收文本并使用该模型根据定义的最大和最小长度参数生成简洁的摘要。

from transformers import pipeline
def summarize_text(text, max_length=130):
  
    # Load a pre-trained summarization model from Hugging Face's model hub.
    # 'facebook/bart-large-cnn' is chosen for its proficiency in generating concise summaries.
    summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
  
    # The summarizer uses the BART model to condense the input text into a summary.
    # 'max_length' specifies the maximum length of the summary output.
    # 'min_length' sets the minimum length to ensure the summary is not too terse.
    # 'do_sample' is set to False to use a deterministic approach for summary generation.
    summary = summarizer(text, max_length=max_length, min_length=30, do_sample=False)
    
    # The output from the summarizer is a list of dictionaries.
    # We extract the summary text from the first dictionary in the list.
    return summary[0]['summary_text']
​
# Example text to be summarized.
# This text discusses the importance of summarization in retrieval-augmented generation systems.
long_text = "Summarization are vital steps in the workflow of retrieval-augmented generation systems. They ensure the output is not only accurate but also concise and digestible. These techniques are essential, especially in domains where the accuracy and precision of information are crucial."
​
# Call the summarize_text function to compress the example text.
summarized_text = summarize_text(long_text)
​
# Print the summarized text to see the output of the summarization model.
print("Summarized Text:", summarized_text)
​

完整的代码,可以参考[3]

4.2 重排序和过滤

       在检索过程中,您应该已经得到每个文档的“分数”——这实际上是向量与查询向量的相似度分数。此信息可用于重新排序文档并根据给定的阈值过滤结果。以下代码块显示了如何重新排序和过滤的示例。

4.2.1. 基本重排序和过滤

       下面代码块定义了一个文档列表,每个文档都由一个包含 ID、文本和相关性分数的字典表示。然后它实现了两个主要功能:re_rank_documents和filter_documents。该re_rank_documents函数按相关性分数降序对文档进行排序,在重新排序后,该filter_documents函数用于排除相关性分数低于指定阈值 0.75 的任何文档。

# Define a list of documents. Each document is represented as a dictionary with an ID, text, and a relevance score.
documents = [
    {"id": 1, "text": "Advanced RAG systems use sophisticated techniques for text summarization.", "relevance_score": 0.82},
    {"id": 2, "text": "Basic RAG systems primarily focus on retrieval and basic processing.", "relevance_score": 0.55},
    {"id": 3, "text": "Re-ranking improves the quality of responses by ordering documents by relevance.", "relevance_score": 0.89}
]
​
# Define a function to re-rank documents based on their relevance scores.
def re_rank_documents(docs):
​
    # Use the sorted function to order the documents by 'relevance_score'.
    # The key for sorting is specified using a lambda function, which extracts the relevance score from each document.
    # 'reverse=True' sorts the list in descending order, placing documents with higher relevance scores first.
    return sorted(docs, key=lambda x: x['relevance_score'], reverse=True)
​
# Re-rank the documents using the defined function and print the result.
ranked_documents = re_rank_documents(documents)
print("Re-ranked Documents:", ranked_documents)
​
# Define a function to filter documents based on a relevance score threshold.
def filter_documents(docs, relevance_threshold=0.75):
  
    # Use a list comprehension to create a new list that includes only those documents whose 'relevance_score'
    # is greater than or equal to the 'relevance_threshold'.
    return [doc for doc in docs if doc['relevance_score'] >= relevance_threshold]
​
# Filter the re-ranked documents using the defined function with a threshold of 0.75 and print the result.
filtered_documents = filter_documents(ranked_documents)
print("Filtered Documents:", filtered_documents)

4.2.2. 使用机器学习算法进行高级重排序

       对于更复杂的方法,可以使用机器学习模型对文档进行重新排序。在这种方法中,挑战在于:如何知道哪些文档是相关的,以便我们可以训练机器学习模型对文档进行排序?

       在这种方法中,我们需要假设我们有一个系统,该系统存储用户与系统之间的交互,并存储文档是否与给定查询相关。一旦我们有了这个数据集,我们就可以使用查询嵌入向量和文档嵌入来预测分数。

# assumung the data is stored in the following format in a database
# query_text | response_text | user_clicked
​
query_embeddings = get_embedding_vector(database.query_text) 
response_embeddings = get_embedding_vector(database.response_text) 
​
# create the dataset
X = concat(query_embeddings, response_embeddings)
y = database.user_clicked
​
model = model.train(X, y)
model.predict_proba(...)

       上面提供的伪代码概述了使用机器学习根据相关性对文档进行重新排序的方法,具体来说,是通过预测用户根据过去的交互找到相关文档的可能性。下面伪代码是对描述流程的分步骤说明:

  • Generating Embeddings(生成嵌入):对于查询和响应文档,创建嵌入向量来捕获它们的语义内容。
  • Creating the Dataset(创建数据集):这些嵌入连接起来形成特征向量(X),目标变量(y)表示用户是否点击了文档。
  • Model Training(模型训练):在该数据集上训练分类模型,以根据组合查询和文档嵌入来预测文档被点击的可能性。
  • Prediction(预测):训练后的模型可以预测新查询-文档对的点击概率,帮助根据预测的相关性重新对文档进行排名,以提高搜索结果的准确性。

五、结论

       实施简单的检索增强生成 (RAG) 系统可能会解决您的问题,但添加增强功能将改善您的结果并帮助您的系统生成更精确的答案。在本文中,我们讨论了旨在实现此目标的几项增强功能,包括数据索引优化、查询增强、混合搜索、嵌入模型的微调、响应汇总以及重新排名和过滤。

       通过集成这些增强功能,您有机会显著提高性能。继续探索和应用这些方法,进行试验,看看哪种方法最适合您的需求。

参考文献:

[1] https://arxiv.org/pdf/2404.10981

[2] https://github.com/ndemir/machine-learning-projects/tree/main/hybrid-search

[3] https://github.com/ndemir/machine-learning-projects/tree/main/fine-tuning-embedding-model

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/682046.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

为什么3D渲染有的模型是黑的?---模大狮模型网

在3D建模和渲染的过程中,遇到模型渲染出来是黑色的情况并不罕见。这可能是由于多种原因导致的,包括但不限于材质设置问题、灯光配置不当、渲染设置错误等。 一、材质设置问题 材质丢失或错误:如果模型在导入或创建时材质没有正确加载或设置&…

力扣 54.螺旋矩阵

题目描述: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]示例 2: 输入&#…

[SaaS] AI+数据,tiktok选品,找达人,看广告数据

TK观察专访丨前阿里“鲁班”创始人用AIGC赋能TikTok获千万融资用AI数据做TikTokhttps://mp.weixin.qq.com/s/xp5UM3ROo48DK4jS9UBMuQ主要还是爬虫做数据的。 商家做内容:1.找达人拍内容,2.商家自己做原生自制内容,3.广告内容。 短视频&…

MariaDB数据导入与导出操作演示

文章目录 整个数据库导出导入先删除库然后再导入 参考这里: MariaDB数据库导出导入. 整个数据库 该部分演示:导出数据库,然后重建数据库,并导入数据的整个过程。 导出 Win R ,打开运行输入cmd并回车,然…

chatglm3-6b小试

原本想在VMware中装个unbutu,再搞chatglm,但经过调研发现业内都是采用双系统来搞chat的开发。于是只好用rufus制作了一个ubuntu22.04的系统盘,你需要准备8G,因为制作好镜像后是7个多G。安装这里就不说了。 1 ubuntu环境 安装好ubu…

python基础篇(2):字符串扩展知识点

1 字符串的三种定义方式 字符串在Python中有多种定义形式: (1)单引号定义法 name 博主帅绝上下五千年 print(name) print(type(name)) 效果如下: (2)双引号定义法 name "博主帅绝上下五千年&qu…

CUDA12.0 + cuDNN9.0.0安装

目录 1. 查看显卡支持的CUDA版本1.1 指令查看1.2 控制面板查看 2. 安装CUDA2.1 下载2.2 安装2.3 验证 3. 安装cuDNN3.1 下载3.2 安装3.2 验证 1. 查看显卡支持的CUDA版本 1.1 指令查看 打开cmd输入nvidia-smiDriver Version表示显卡驱动版本,CUDA Version表示支持…

【AndroidStudio旧版本BUG问题】完美解决运行报错问题Invalid keystore format

由于之前安装的版本导致AndroidStudio 运行报错:Invalid keystore format 在如下截图的路径中删了debug.keystore重新打开Android Studio运行一下就好了!!! 下面介绍各个模块功能: adbkey 是 Android Debug Bridge (AD…

【数据库】SQL--DQL(初阶)

文章目录 DCL1. 基本介绍2. 语法2.1 基础查询2.2 条件查询2.3 聚合函数2.4 聚合查询2.5 分组查询2.6 排序查询2.7 分页查询2.8 综合案例练习2.9 执行顺序 3. DQL总结 DCL 更多数据库MySQL系统内容就在以下专栏: 专栏链接:数据库MySQL 1. 基本介绍 DQL英…

Linux下SpringBoot项目部署(centos系统)

一、首先找到自己的sql文件,没有就从数据库挪进来 二、在Maven下打包一下(点击package),看到BUILD SUCCESS就是打包好了 三、将上面两个文件分别挪到 linux 中对应的文件,没有就创建一个(我的是spring_blog…

【数据库初阶】SQL--DCL

文章目录 DCL1. 基本介绍2. 用户管理2.1 查询用户2.2 创建用户2.3 修改用户密码2.4 删除用户 3. 权限控制3.1 查询权限3.2 授予权限3.3 撤销权限 4. DCL总结 DCL 更多数据库MySQL系统内容就在以下专栏: 专栏链接:数据库MySQL 1. 基本介绍 DCL英文全称是…

Docker 部署 Redis Cluster 高性能高可用分片集群

文章目录 1、环境准备2、Cluster 集群讲解2.1、Cluster 介绍2.2、Cluster 和哨兵模式区别2.3、Cluster 如何分散存储数据 3、Cluster 搭建流程3.1、安装 Docker3.2、启动 Redis 容器3.3、创建 Cluster 集群 4、Cluster 集群测试4.1、读写操作4.2、故障转移 1、环境准备 准备6台…

计算机网络 ——数据链路层(广域网)

计算机网络 —— 广域网 什么是广域网PPP协议PPP协议的三个部分PPP协议的帧格式 HDLC协议HDLC的站HDLC的帧样式 PPP和HDLC的异同 我们今天来看广域网。 什么是广域网 广域网(Wide Area Network,简称WAN)是一种地理覆盖范围广泛的计算机网络…

php实现抖音小程序支付

开发者发起下单_小程序_抖音开放平台 第一步、抖音小程序发起支付 tt.pay_小程序_抖音开放平台 前端提交订单数据到后端接口,然后使用 tt.pay发起支付 请求参数 属性 类型 必填 说明 order_id string 是 担保交易服务端订单号 order_token string 是 …

css动画案例练习之会展开的魔方和交错的小块

这里写目录标题 一级目录二级目录三级目录 下面开始案例的练习,建议第一个动手操作好了再进行下一个一、交错的小块效果展示1.大致思路1.基本结构2.实现动态移动 2.最终版代码 二、会展开的魔方1.大致思路1.基本结构;2.静态魔方的构建3.让静态的魔方动起来 2.最终版…

Springboot高手之路01-AOP

文章目录 登录校验拦截器 基于拦截器实现登录校验功能全局异常处理器 登录校验 拦截器 拦截器是spring中提供的 所以第一步就是把拦截器交给spring管理 不管是过滤器还是拦截器都是需要配置路径 /** 拦截所有 拦截器放行直接是true 是否运行访问对应的web资源 拦截器实现Ha…

动态规划6:63. 不同路径 II

动态规划解题步骤: 1.确定状态表示:dp[i]是什么 2.确定状态转移方程:dp[i]等于什么 3.初始化:确保状态转移方程不越界 4.确定填表顺序:根据状态转移方程即可确定填表顺序 5.确定返回值 题解:63. 不同…

通用漏洞-基于同源策略的cors与jsonp域名接管

脚本参考:https://www.cnblogs.com/haorancracker/articles/17699536.html) 1.cors&jsonp 在csrf的学习中,我们了解到解决第三方网站采取的发包操作,最简单的策略就是同源策略sof,即为同协议,同域名,…

C++设计模式-单例模式,反汇编

文章目录 25. 单例模式25.1. 饿汉式单例模式25.2. 懒汉式单例模式25.2.1. 解决方案125.2.2. 解决方案2 (推荐写法) 运行在VS2022,x86,Debug下。 25. 单例模式 单例即该类只能有一个实例。 应用:如在游戏开发中&#x…

VS2022,DLL1调用lib,lib调用DLL2

DLL1调用lib,lib调用DLL2 问题1:为什么在dll1中需要引入dll2的.lib文件 当你有一个工程(dll1)调用静态库(lib),而静态库(lib)又调用另一个DLL(dll2&#xf…